Anyone know of a MICR parser algorithm written in Python?

mkppk barnaclejive at gmail.com
Sun Mar 25 14:04:53 EDT 2007


On Mar 25, 12:30 am, "Paul McGuire" <p... at austin.rr.com> wrote:
> On Mar 24, 6:52 pm, "mkppk" <barnaclej... at gmail.com> wrote:
>
> > Its just that I would rather not reinvent the wheel (or read old C
> > code)..
>
> Wouldn't we all!
>
> Here is the basic structure of a pyparsing solution.  The parsing part
> isn't so bad - the real problem is the awful ParseONUS routine in C.
> Plus things are awkward since the C program parses right-to-left and
> then reverses all of the found fields, and the parser I wrote works
> left-to-right.  Still, this grammar does most of the job.  I've left
> out my port of ParseONUS since it is *so* ugly, and not really part of
> the pyparsing example.
>
> -- Paul
>
> from pyparsing import *
>
> # define values for optional fields
> NoAmountGiven  = ""
> NoEPCGiven     = ""
> NoAuxOnusGiven = ""
>
> # define delimiters
> DOLLAR = Suppress("$")
> T_ = Suppress("T")
> A_ = Suppress("A")
>
> # field definitions
> amt = DOLLAR + Word(nums,exact=10) + DOLLAR
> onus = Word("0123456789A- ")
> transit = T_ + Word("0123456789-") + T_
> epc = oneOf( list(nums) )
> aux_onus = A_ + Word("0123456789- ") + A_
>
> # validation parse action
> def validateTransitNumber(t):
>     transit = t[0]
>     flds = transit.split("-")
>     if len(flds) > 2:
>         raise ParseException(0, "too many dashes in transit number",
> 0)
>     if len(flds) == 2:
>         if len(flds[0]) not in (3,4):
>             raise ParseException(0, "invalid dash position in transit
> number", 0)
>     else:
>         # compute checksum
>         ti = map(int,transit)
>         ti.reverse() # original algorithm worked with reversed data
>         cksum = 3*(ti[8]+ti[5]+ti[2]) + 7*(ti[7]+ti[4]+ti[1]) +
> ti[6]+ti[3]+ti[0]
>         if cksum%10 != 0:
>             raise ParseException(0, "transit number failed checksum",
> 0)
>     return transit
>
> # define overallMICRformat, with results names
> micrdata =
> Optional(aux_onus,default=NoAuxOnusGiven).setResultsName("aux_onus") +
> \
>             Optional(epc,default=NoEPCGiven).setResultsName("epc") +\
>
> transit.setParseAction(validateTransitNumber).setResultsName("transit")
> + \
>             onus.setResultsName("onus") + \
>             Optional(amt,default=NoAmountGiven).setResultsName("amt")
> + \
>             stringEnd
>
> import re
>
> def parseONUS(tokens):
>     tokens["csn"] = ""
>     tokens["tpc"] = ""
>     tokens["account"] = ""
>     tokens["amt"] = tokens["amt"][0]
>     onus = tokens.onus
>     # remainder omitted out of respect for newsreaders...
>     # suffice to say that unspeakable acts are performed on
>     # onus and aux_onus fields to extract account and
>     # check numbers
>
> micrdata.setParseAction(parseONUS)
>
> testdata = file("checks.csv").readlines()[1:]
> tests = [(flds[1],flds) for flds in map(lambda
> l:l.split(","),testdata)]
> def verifyResults(res,csv):
>     def match(x,y):
>         print (x==y and "_" or "X"),x,"=",y
>
> Ex,MICR,Bank,Stat,Amt,AS,TPC,TS,CSN,CS,ACCT,AS,EPC,ES,ONUS,OS,AUX,AS,Tran,TS
> = csv
>     match(res.amt,Amt)
>     match(res.account,ACCT)
>     match(res.csn,CSN)
>     match(res.onus,ONUS)
>     match(res.tpc,TPC)
>     match(res.epc,EPC)
>     match(res.transit,Tran)
>
> for t,data in tests:
>     print t
>     try:
>         res = micrdata.parseString(t)
>         print res.dump()
>         if not(data[0] == "No"):
>             print "Passed expression that should have failed"
>         verifyResults(res,data)
>     except ParseException,pe:
>         print "<parse failed> %s" % pe.msg
>         if not(data[0] == "Yes"):
>             print "Failed expression that should have passed"
>     print


Great, thanks for taking a look Paul. I had never tried to use
pyparsing before. Yea, the ONUS field is crazy, don't know why there
is no standard for it.




More information about the Python-list mailing list