2010-02-01 2 views
3

J'utilise Python 2.6.4.expression imbriquée récursive en Python

J'ai une série d'instructions select dans un fichier texte et j'ai besoin d'extraire les noms de champs de chaque requête select. Cela serait facile si certains des champs n'utilisaient pas de fonctions imbriquées comme to_char() etc.

Étant donné les champs d'instructions select qui pourraient avoir plusieurs parenthèses imbriquées comme "ltrim (rtrim (to_char (nom_de_base_de_fondation, format))) renamed_field_name , "ou le cas simple de simplement" base_field_name "comme un champ, est-il possible d'utiliser le module re de Python pour écrire une regex pour extraire base_field_name? Si oui, à quoi ressemblerait l'expression rationnelle?

Répondre

2
>>> import re 
>>> string = 'ltrim(rtrim(to_char(base_field_name, format))) renamed_field_name' 
>>> rx = re.compile('^(.*?\()*(.+?)(,.*?)*(,|\).*?)*$') 
>>> rx.search(string).group(2) 
'base_field_name' 
>>> rx.search('base_field_name').group(2) 
'base_field_name' 
+2

PS: comme dit par Alex Martelli, vous devez utiliser un vrai analyseur ici. Quoi qu'il en soit, si vous voulez seulement une regex rapide qui fonctionne, vous pouvez l'utiliser. Mais vous devriez vraiment utiliser un analyseur, comme cette regex semble plutôt moche :) –

+0

Je ne suis pas à la recherche de quelque chose qui a l'air joli puisque c'est un pour m'obtenir les données que je veux afin que je puisse faire d'autres choses avec. :) Mais merci, mon regex est rouillée et j'ai pensé que quelqu'un pourrait mieux savoir. – TheObserver

11

Les expressions régulières ne sont pas adaptées à l'analyse de structures "imbriquées". Essayez, à la place, un kit d'analyse complet tel que pyparsing - des exemples d'utilisation de pyparsing spécifiquement pour analyser SQL peuvent être trouvés here et here, par exemple (vous aurez sans doute besoin de prendre les exemples comme point de départ, et écrire du code d'analyse, mais ce n'est certainement pas trop difficile).

+1

+1 pour se souvenir du monde que les expressions bien (bien parenthésées, toutes les langues de type 2 Chomsky) ont besoin de plus d'un regexp être correctement analysé :) – Agos

2

Soit un analyseur par des tables comme Alex Martelli suggère ou un analyseur de descente récursive écrite à la main. Ils ne sont pas durs et très enrichissants à écrire.

1

Cela peut être assez bon:

import re 
print re.match(r".*\(([^\)]+)\)", "ltrim(to_char(field_name, format)))").group(1) 

Vous devez faire un traitement ultérieur. Par exemple, prenez le nom de la fonction et tirez le nom du champ en fonction de la signature de la fonction.

.*(\w+)\(([^\)]+)\) 
+0

cela imprime 'field_name, format', pas 'field_name' pour moi, et ne fonctionne pas non plus pour la chaîne simple 'field_name'. –

+0

Comment savez-vous que chaque fonction va accepter les mêmes arguments? – ziya

0

Avez-vous vraiment besoin d'expressions régulières? Pour obtenir celui que vous avez là-haut, j'utiliserais

s[s.rfind('(')+1:s.find(')')].split(',')[0] 

avec 's' contenant la chaîne d'origine.

Bien sûr, ce n'est pas une solution générale, mais ...

+0

Une regex compilé devrait être beaucoup plus rapide que cela. Eh bien, je suppose que nous ne sommes pas pressés, mais tout de même, par souci d'efficacité. –

+0

Vous pouvez trouver que travailler directement avec des chaînes est plus rapide. Dépend fortement de l'expression rationnelle et de la complexité du code équivalent que vous devez écrire pour faire les choses sans regex. En fait, avez-vous essayé de les comparer tous les deux? –

+1

Oh, et dans le cas où je voudrais aller à l'expression rationnelle équivalente, j'utiliserais "\\ (([^(),] +),", qui est légèrement plus rapide que celui purement basé sur les chaînes. Les deux sont d'un ordre de grandeur plus rapide que votre regexp ... –

1

est ici un analyseur vraiment aki qui fait ce que vous voulez. Il fonctionne en appelant 'eval' sur le texte à analyser, en mappant tous les identifiants à une fonction qui renvoie son premier argument (ce que je suppose est ce que vous voulez, étant donné votre exemple).

class FakeFunction(object): 
    def __init__(self, name): 
     self.name = name 
    def __call__(self, *args): 
     return args[0] 
    def __str__(self): 
     return self.name 

class FakeGlobals(dict): 
    def __getitem__(self, x): 
     return FakeFunction(x) 

def ExtractBaseFieldName(x): 
    return eval(x, FakeGlobals()) 

print ExtractBaseFieldName('ltrim(rtrim(to_char(base_field_name, format)))')