J'utilise actuellement le module re
de python pour rechercher et capturer des groupes . J'ai une liste d'expressions régulières que je dois compiler et faire correspondre à un grand ensemble de données qui pose des problèmes de performances.
Example:
REGEXES = [
'^New York(?P<grp1>\d+/\d+): (?P<grp2>.+)$',
'^Ohio (?P<grp1>\d+/\d+/\d+): (?P<grp2>.+)$',
'(?P<year>\d{4}-\d{1,2}-\d{1,2})$',
'^(?P<year>\d{1,2}/\d{1,2}/\d{2,4})$',
'^(?P<title>.+?)[- ]+E(?P<epi>\d+)$'
.
.
.
.
]
Note: Les expressions rationnelles ne seront pas similaires
COMPILED_REGEXES = [re.compile(r, flags=re.I) for r in REGEXES]
def find_match(string):
for regex in COMPILED_REGEXES:
match = regex.search(string)
if not match:
continue
return match
Y a-t-il un moyen de contourner ceci? L'idée est d'éviter les itérations dans les expressions rationnelles compilées pour obtenir une correspondance.
Est-ce que l'un de vos regexps rompt la compatibilité DFA? Cela ne ressemble pas à cela dans vos exemples. Vous pouvez utiliser un wrapper Python autour d'une implémentation DFA C/C++ telle que re2 , qui remplace avantageusement re
. re2
utilisera également re
si l'expression régulière est incompatible avec le re2
syntaxe , afin d'optimiser tous les cas possibles et de ne pas échouer dans les cas incompatibles.
Notez que re2
ne supporte la syntaxe de capture (?P<name>regex)
, mais ne ne prend pas en charge le (?P=<name>)
backref sytnax.
try:
import re2 as re
re.set_fallback_notification(re.FALLBACK_WARNING)
except ImportError:
# latest version was for Python 2.6
else:
import re
Si vous avez des expressions rationnelles avec des backrefs, vous pouvez toujours utiliser re2
avec quelques considérations spéciales: vous devrez remplacer les backrefs dans vos regexps par .*?
et vous pourrez trouver de fausses correspondances que vous pourrez filtrer avec re
. Dans les données du monde réel, les faux appariements seront probablement rares.
Voici un exemple illustratif:
import re
try:
import re2
re2.set_fallback_notification(re2.FALLBACK_WARNING)
except ImportError:
# latest version was for Python 2.6
REGEXES = [
'^New York(?P<grp1>\d+/\d+): (?P<grp2>.+)$',
'^Ohio (?P<grp1>\d+/\d+/\d+): (?P<grp2>.+)$',
'(?P<year>\d{4}-\d{1,2}-\d{1,2})$',
'^(?P<year>\d{1,2}/\d{1,2}/\d{2,4})$',
'^(?P<title>.+?)[- ]+E(?P<epi>\d+)$',
]
COMPILED_REGEXES = [re.compile(r, flags=re.I) for r in REGEXES]
# replace all backrefs with .*? for re2 compatibility
# is there other unsupported syntax in REGEXES?
COMPILED_REGEXES_DFA = [re2.compile(re2.sub(r'\\d|\\g\\d|\\g\<\d+\>|\\g\<\w+\>', '.*?', r), flags=re2.I) for r in REGEXES]
def find_match(string):
for regex, regex_dfa in Zip(COMPILED_REGEXES, COMPILED_REGEXES_DFA):
match_dfa = regex_dfa.search(string)
if not match_dfa:
continue
match = regex.search(string)
# most likely branch comes first for better branch prediction
if match:
return match
Si cela n’est pas assez rapide, vous pouvez utiliser diverses techniques pour alimenter les hits DFA en re
au fur et à mesure de leur traitement, au lieu de les stocker dans un fichier ou en mémoire et de les remettre une fois qu’ils ont été collectés.
Vous pouvez également combiner toutes vos expressions rationnelles en une seule grande expression rationnelle DFA de groupes alternatifs (r1)|(r2)|(r3)| ... |(rN)
et parcourir les correspondances de groupe sur l'objet de correspondance obtenu pour essayer de ne faire correspondre que les expressions rationnelles correspondantes. L'objet résultat de la correspondance aura le même état que la solution d'origine de OP.
# rename group names in regexeps to avoid name collisions
REGEXES_PREFIXED = [re2.sub(r'\(\?P\<(\w+)\>', r'(P<re{}_\1>'.format(idx), r) for idx, r in enumerate(REGEXES)]
# wrap and fold regexps (?P<hit0>pattern)| ... |(?P<hitN>pattern)
REGEX_BIG = ''
for idx, r in enumerate(REGEXES_PREFIXED):
REGEX_BIG += '(?P<hit{}>{})|'.format(idx, r)
else:
REGEX_BIG = REGEX_BIG[0:-1]
regex_dfa_big = re2.compile(REGEX_BIG, flags = re2.I)
def find_match(string):
match_dfa = regex_dfa_big.search(string)
if match_dfa:
# only interested in hit# match groups
hits = [n for n, _ in match_dfa.groupdict().iteritems() if re2.match(r'hit\d+', n)]
# check for false positives
for idx in [int(h.replace('hit', '')) for h in hits]
match = COMPILED_REGEXES[idx].search(string)
if match:
return match
Vous pouvez aussi regarder pyre } _ qui est un wrapper mieux maintenu pour la même bibliothèque C++, mais pas une substitution pour re
. Il y a aussi un Python Wrapper pour RuRe , qui est le moteur de regex le plus rapide que je connaisse.
Pour élaborer mon commentaire: le problème avec tout mettre dans une grande expression rationnelle est que les noms de groupe doivent être uniques. Cependant, vous pouvez traiter vos expressions rationnelles comme suit:
import re
REGEXES = [
r'^New York(?P<grp1>\d+/\d+): (?P<grp2>.+)$',
r'^Ohio (?P<grp1>\d+/\d+/\d+): (?P<grp2>.+)$',
r'(?P<year>\d{4}-\d{1,2}-\d{1,2})$',
r'^(?P<year>\d{1,2}/\d{1,2}/\d{2,4})$',
r'^(?P<title>.+?)[- ]+E(?P<epi>\d+)$']
# Find the names of groups in the regexps
groupnames = {'RE_%s'%i:re.findall(r'\(\?P<([^>]+)>', r) for i, r in enumerate(REGEXES)}
# Convert the named groups into unnamed ones
re_list_cleaned = [re.sub(r'\?P<([^>]+)>', '', r) for r in REGEXES]
# Wrap each regexp in a named group
token_re_list = ['(?P<RE_%s>%s)'%(i, r) for i, r in enumerate(re_list_cleaned)]
# Put them all together
mighty_re = re.compile('|'.join(token_re_list), re.MULTILINE)
# Use the regexp to process a big file
with open('bigfile.txt') as f:
txt = f.read()
for match in mighty_re.finditer(txt):
# Now find out which regexp made the match and put the matched data in a dictionary
re_name = match.lastgroup
groups = [g for g in match.groups() if g is not None]
gn = groupnames[re_name]
matchdict = dict(Zip(gn, groups[1:]))
print ('Found:', re_name, matchdict)
Je suggère de faire les étapes suivantes:
Créez un fichier Excel appelé Patterns.csv contenant deux colonnes: Patterns & Name, où motif est le motif d'expression régulière tel que ^New York(?P<grp1>\d+/\d+): (?P<grp2>.+)$'
et nom peut être New York
. Cela vous aidera à conserver toutes les expressions rationnelles dans une ressource distincte autre que votre code. Cela vous aidera à l’avenir si vous souhaitez ajouter/soustraire/modifier des expressions rationnelles.
Lisez que csv en utilisant la commande ci-dessous:
import pandas as pd
df = pd.read_csv("\\Patterns.csv")
Écrivez le code pour analyser ce csv comme ci-dessous:
pattern = df['pattern'].tolist()
pattern_name = df['name'].tolist()
pattern_dict = dict(Zip(pattern_name, pattern))
Ecrivez un motif regex pour trouver toutes les valeurs qui correspondent:
import collections
sep = " ;; "
NLU_Dict=collections.defaultdict()
for pn, p in pattern_dict.items():
val = sep.join([sep.join(filter(lambda x: len(str(x).strip()) >0, map(str, v))) for in re.findall(p, text, re.I)])
NLU_Dict[pn] = val
Votre NLU_Dict
sera un dictée. séparés par ;;
contenant les valeurs des noms de modèle qui correspondent et vides pour ce qui ne correspond pas.
Je regarderais re.Scanner
. C'est non documenté et marqué comme expérimental, mais c'est un bon exemple d'utilisation de sre_parse
et sre_compile
pour construire une expression régulière en analysant, en fusionnant, puis en compilant. Si vous ne vous souciez pas des noms de groupe et que vous ne voulez capturer que des groupes, cela devrait fonctionner. Remarquez, ce code n'a pas d'erreur de vérification.
import re
import sre_parse
import sre_compile
def compile_multiple(subpatterns, flags=0):
"""
Return a compiled regex from an iterable collection of
pattern strings so that it matches any of the patterns
in the collection.
"""
from sre_constants import BRANCH, SUBPATTERN
if isinstance(flags, re.RegexFlag):
flags = flags.value
pattern = sre_parse.Pattern()
pattern.flags = flags
parsed_subpatterns = []
for subpattern in subpatterns:
gid = pattern.opengroup()
parsed_subpattern = sre_parse.parse(subpattern, flags)
parsed_subpatterns.append(sre_parse.SubPattern(pattern, [
(SUBPATTERN, (gid, 0, 0, sre_parse.parse(subpattern, flags))),
]))
pattern.closegroup(gid, parsed_subpatterns[-1])
combined_pattern = sre_parse.SubPattern(pattern, [(BRANCH, (None, parsed_subpatterns))])
return sre_compile.compile(combined_pattern)