The pyparsing module is an alternative approach to creating and executing simple grammars, vs. the traditional lex/yacc approach, or the use of regular expressions. The pyparsing module provides a library of classes that client code uses to construct the grammar directly in Python code. pyparsing - home
C'est vrai. Il y a aussi un livre d'O'Reilly, donc je pense que les riches devraient l'acheter. Je l'ai recherché et écrit parce qu'il ne semble y avoir aucune information en japonais. Je l'essaie dans un environnement Python 2.7, donc ce n'est peut-être pas moderne. La version de pyparsing utilisée était la 2.0.2.
.parseString
de l'analyseur et évaluez le résultatc'est tout.
Il existe deux façons de créer un analyseur. L'une consiste à créer une instance d'une classe dérivée «ParserElement», et l'autre est de créer un «ParserElement» approprié en utilisant une fonction de générateur à partir d'un module.
import pyparsing as pp
word = pp.Word(pp.alphanums)
comma = pp.Literal(',')
csv = word + pp.ZeroOrMore(comma + word)
# >>> csv.parseString("1,2,3,a,5")
(['1', ',', '2', ',', '3', ',', 'a', ',', '5'], {})
Certaines classes dérivées prennent des chaînes et des expressions régulières comme arguments pour les instances, et d'autres prennent la classe pyparsing elle-même comme argument.
Word
, Literal
, etc.
--Classes: ʻOneOrMore,
NotAny,
SkipTo`, etc.Certains opérateurs sont fournis pour ceux qui prennent (une liste de) classes. Il n'y a pas de classe correspondante, mais vous pouvez exprimer la répétition avec *
. Le nombre à multiplier et le nombre à multiplier peuvent être interchangés, mais l'un doit être «int »de 0 ou plus. ("Long" n'est pas accepté)
pp.And([p1, p2]) #== p1 + p2 ;Jointure ordonnée
pp.Each([p1, p2]) #== p1 & p2 ;Jointure non ordonnée
pp.MatchFirst([p1, p2]) #== p1 | p2 ;Match prioritaire
pp.Or([p1, p2]) #== p1 ^ p2 ;Match le plus long
pp.NotAny(p) #== ~ p ;le déni
p + p + p #== p * 3 or 3 * p ;Abréviation de liaison
Ce n'est peut-être pas une nomenclature très intelligente, mais il faut s'y habituer. De plus, bien que «chaque» soit dans le désordre, il ne revient pas en arrière et est essayé dans l'ordre depuis le début, donc s'il y a une partie qui chevauche chaque élément, l'entrée ne peut pas être mangée et une exception est émise. Si vous voulez vraiment utiliser l'ensemble de somme, utilisez modérément Match First
.
La fonction de générateur était à l'origine spécialisée dans un but précis et était composée en interne d'expressions régulières. Si tout cela se produit, vous devriez penser que la politique de conception de l'analyseur est erronée. Je vais l'omettre ici.
Essayez d'analyser la ligne $ GPGSV de la sortie NMEA 0183 du récepteur GPS.
$ GPGSV a la structure suivante.
$GPGSV,3,2,12,16,02,229,,22,21,224,16,24,02,095,,25,52,039,35*73\r\n
Il commence par le caractère de début de «$» et se termine par la séquence de fin de «\ r \ n». Une somme de contrôle au format *% 2h
est insérée avant la séquence de fin. Le reste contient 7 à 16 valeurs séparées par des virgules (éventuellement vides).
Le nombre total ne peut pas être déterminé à partir du message. Le message est divisé et envoyé, et la quantité d'informations à transmettre est décrite, de sorte qu'elle peut être déterminée par calcul, mais cette fois elle ne sera pas effectuée jusqu'à présent. (Le deuxième champ est le nombre total de messages, le troisième est le numéro du message actuel et le quatrième est la quantité d'informations (nombre de satellites))
buf = '$GPGSV,3,2,12,16,02,229,,22,21,224,16,24,02,095,,25,52,039,35*73\r\n'
# checksum (for validation)
ck = "{:02X}".format(reduce(lambda a,b: a^b, [ord(_) for _ in buf[1+buf.find("$"):buf.rfind("*")]]))
# simple parser unit
toint = lambda a: int(a[0])
c = pp.Literal(',').suppress()
nemo = pp.Literal('$GPGSV')
numMsg = pp.Word(pp.nums).setParseAction(toint)
msgNum = pp.Word(pp.nums).setParseAction(toint).setResultsName("msgNum")
numSV = pp.Word(pp.nums).setParseAction(toint).setResultsName("numSV")
sv = pp.Word(pp.nums).setParseAction(toint)
# combinated parser unit
toint_maybe = lambda a: int(a[0]) if a[0] else -1
elv = pp.Combine(pp.Word(pp.nums) ^ pp.Empty()).setParseAction(toint_maybe)
az = pp.Combine(pp.Word(pp.nums) ^ pp.Empty()).setParseAction(toint_maybe)
cno = pp.Combine(pp.Word(pp.nums) ^ pp.Empty()).setParseAction(toint_maybe)
cs = pp.Combine(pp.Literal('*') + pp.Literal(ck)).suppress()
block = pp.Group(c + sv + c + elv + c + az + c + cno)
blocks = pp.OneOrMore(block)
parser = nemo + c + numMsg + c + msgNum + c + numSV + blocks + cs
# result
ret = parser.parseString(buf)
La seule chose à retenir est Word (" ab ... ") == Each ([Literal ('a'), Literal ('b'), ...])
. Puisque Literal
peut contenir des caractères d'une longueur de 1 ou plus, il est recommandé d'alimenter des chaînes de caractères fixes avec this et de consommer des valeurs numériques avec Word
.
Le résultat analysé en «ret» est renvoyé. Pour le moment, celui qui peut être converti en entier est converti en entier avec setParseAction
. Sinon, il renvoie simplement une liste de chaînes. Si vous voulez manger le tampon mais ne voulez pas qu'il reste dans le résultat, vous pouvez le supprimer en utilisant la méthode .suppress ()
ou en enveloppant l'élément avec pp.Suppress
. Il n'est pas utilisé efficacement, mais il peut être nommé avec la méthode .setResultsName
.
Comme lieu pratique ou addictif
La valeur obtenue par la méthode finale parseString
est une liste, mais la rupture de cet élément est chaque ParserElement ou l'unité explicitement regroupée par Combine
. Par conséquent, si vous combinez simplement en utilisant «Et», la correspondance entre la formule d'assemblage de l'analyseur final et le résultat sera étrange. Vous devriez faire «Combiner» modérément.
Group
, vous ne pouvez pas maintenir le morceau quand il est répété avec ʻOneOrMore` etc.Il est lié à la combinaison de Combine
, mais si rien n'est fait, le résultat sera une liste de chaînes. Il n'est pas imbriqué, vous ne connaîtrez donc pas le résultat lors d'une mauvaise journée comme ZeroOrMore
. Utilisez Group
lorsque vous souhaitez imbriquer. Si vous pensez utiliser setParseAction
, vous l'utiliserez certainement.
ParserElement
est non destructive et renvoie une copieVous ne pouvez donc pas l'écrire de manière déclarative, mais vous pouvez créer une chaîne de méthodes à la place.
setParseAction
essaie la fonction avec try-catch
Selon le document, certains prototypes sont définis, mais pour chaque prototype, définissez des arguments et lancez, s'il échoue, essayez le suivant, et s'il s'agit d'une erreur jusqu'à la fin, la dernière erreur est renvoyée comme l'erreur entière, donc elle est mal interne Lorsqu'une exception se produit, il est extrêmement difficile de déboguer. Même si le traitement des arguments est incorrect, la dernière erreur sera une erreur de prototype avec des arguments insuffisants, vous ne saurez donc pas ce que vous dites.
c'est tout.