Pyparsing - где порядок токенов непредсказуем

StackOverflow https://stackoverflow.com/questions/2134416

  •  22-09-2019
  •  | 
  •  

Вопрос

Я хочу иметь возможность извлекать тип и количество букв из фрагмента текста, где буквы могут быть в любом порядке.Есть еще какой-то анализ, над которым я работаю, но этот момент поставил меня в тупик!

input -> result
"abc" -> [['a',1], ['b',1],['c',1]]
"bbbc" -> [['b',3],['c',1]]
"cccaa" -> [['a',2],['c',3]]

Я мог бы использовать поиск или сканирование и повторить для каждой возможной буквы, но есть ли простой способ сделать это?

Это насколько я понял:

from pyparsing import *


def handleStuff(string, location, tokens):

        return [tokens[0][0], len(tokens[0])]


stype = Word("abc").setParseAction(handleStuff)
section =  ZeroOrMore(stype("stype"))


print section.parseString("abc").dump()
print section.parseString("aabcc").dump()
print section.parseString("bbaaa").dump()
Это было полезно?

Решение

Из вашего описания мне не было ясно, можно ли смешивать входные символы, например «ababc», поскольку во всех ваших тестовых примерах буквы всегда были сгруппированы вместе.Если буквы являются всегда сгруппированы вместе, вы можете использовать этот код pyparsing:

def makeExpr(ch):
    expr = Word(ch).setParseAction(lambda tokens: [ch,len(tokens[0])])
    return expr

expr = Each([Optional(makeExpr(ch)) for ch in "abc"])

for t in tests:
    print t,expr.parseString(t).asList()

Конструкция Each обеспечивает неупорядоченное сопоставление, а Word(ch) обрабатывает повторение от 1 до n.Действие синтаксического анализа обеспечивает преобразование проанализированных токенов в кортежи (символ, количество).

Другие советы

Одно решение:

text = 'sufja srfjhvlasfjkhv lasjfvhslfjkv hlskjfvh slfkjvhslk'
print([(x,text.count(x)) for x in set(text)])

Никакого pyparsing не требуется, но это кажется излишним.

Мне нравится Однострочное решение Леннарта.

Алекс упоминает еще один отличный вариант если вы используете 3.1

Еще один вариант коллекции.defaultdict:

>>> from collections import defaultdict
>>> mydict = defaultdict(int)
>>> for c in 'bbbc':
...   mydict[c] += 1
...
>>> mydict
defaultdict(<type 'int'>, {'c': 1, 'b': 3})

Если вам нужен подход, основанный на чистом пипарсинге, это кажется правильным:

from pyparsing import *

# lambda to define expressions
def makeExpr(ch):
    expr = Literal(ch).setResultsName(ch, listAllMatches=True)
    return expr

expr = OneOrMore(MatchFirst(makeExpr(c) for c in "abc"))
expr.setParseAction(lambda tokens: [[a,len(b)] for a,b in tokens.items()])


tests = """\
abc
bbbc
cccaa
""".splitlines()

for t in tests:
    print t,expr.parseString(t).asList()

Распечатки:

abc [['a', 1], ['c', 1], ['b', 1]]
bbbc [['c', 1], ['b', 3]]
cccaa [['a', 2], ['c', 3]]

Но это начинает затрагивать неясную область кода, поскольку он основан на некоторых наиболее загадочных функциях pyparsing.В общем, мне нравятся счетчики частоты, использующие defaultdict (еще не пробовал Counter), поскольку там довольно ясно, что вы делаете.

pyparsing отдельно - в Python 3.1, коллекции.Счетчик делает такие задачи по подсчету действительно простыми.Хорошая версия Counter для Python 2 можно найти здесь.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top