J'essaie d'utiliser python pour analyser des lignes de code source c ++. La seule chose qui m'intéresse est d'inclure des directives.
#include "header.hpp"
Je veux qu'il soit flexible et fonctionne toujours avec des styles de codage médiocres comme:
# include"header.hpp"
J'en suis arrivé au point où je peux lire les lignes et couper les espaces avant et après le #. Cependant, j'ai encore besoin de savoir de quelle directive il s'agit en lisant la chaîne jusqu'à ce qu'un caractère non alpha soit rencontré, quelle que soit la météo, qu'il s'agisse d'un espace, d'une citation, d'une tabulation ou d'une parenthèse angulaire.
Donc, fondamentalement, ma question est: comment puis-je diviser une chaîne commençant par alphas jusqu'à ce qu'un non alpha soit rencontré?
Je pense que je pourrais être en mesure de le faire avec regex, mais je n'ai rien trouvé dans la documentation qui ressemble à ce que je veux.
Aussi, si quelqu'un a des conseils sur la façon dont j'obtiendrais le nom du fichier entre guillemets ou crochets, ce serait un plus.
Vous pouvez le faire avec une expression régulière. Cependant, vous pouvez également utiliser une simple boucle while
.
def splitnonalpha(s):
pos = 1
while pos < len(s) and s[pos].isalpha():
pos+=1
return (s[:pos], s[pos:])
Tester:
>>> splitnonalpha('#include"blah.hpp"')
('#include', '"blah.hpp"')
Votre instinct pour utiliser l'expression régulière est correct.
import re
re.split('[^a-zA-Z]', string_to_split)
Le [^a-zA-Z]
partie signifie "caractères non alphabétiques".
Les deux options mentionnées par d'autres qui sont les meilleures à mon avis sont re.split
et re.findall
:
>>> import re
>>> re.split(r'\W+', '#include "header.hpp"')
['', 'include', 'header', 'hpp', '']
>>> re.findall(r'\w+', '#include "header.hpp"')
['include', 'header', 'hpp']
Une référence rapide:
>>> setup = "import re; Word_pattern = re.compile(r'\w+'); sep_pattern = re.compile(r'\W+')"
>>> iterations = 10**6
>>> timeit.timeit("re.findall(r'\w+', '#header foo bar!')", setup=setup, number=iterations)
3.000092029571533
>>> timeit.timeit("Word_pattern.findall('#header foo bar!')", setup=setup, number=iterations)
1.5247418880462646
>>> timeit.timeit("re.split(r'\W+', '#header foo bar!')", setup=setup, number=iterations)
3.786440134048462
>>> timeit.timeit("sep_pattern.split('#header foo bar!')", setup=setup, number=iterations)
2.256173849105835
La différence fonctionnelle est que re.split
conserve les jetons vides. Cela n'est généralement pas utile à des fins de tokenisation, mais les éléments suivants doivent être identiques à re.findall
Solution:
>>> filter(bool, re.split(r'\W+', '#include "header.hpp"'))
['include', 'header', 'hpp']
Vous pouvez utiliser l'expression régulière. Le \W
le jeton correspondra à tous les caractères non Word (ce qui est à peu près le même que non alphanumérique). Les caractères des mots sont A-Z
, a-z
, 0-9
, et _
. Si vous souhaitez également faire correspondre les traits de soulignement, vous pouvez simplement faire [\W_]
.
>>> import re
>>> line = '# include"header.hpp" '
>>> m = re.match(r'^\s*#\s*include\W+([\w\.]+)\W*$', line)
>>> m.group(1)
'header.hpp'
import re
s = 'foo bar- blah/hm.lala'
print(re.findall(r"\w+",s))
sortie: ['foo', 'bar', 'blah', 'hm', 'lala']
Bien qu'elles ne soient pas exactes, la plupart des directives d'en-tête d'analyse comme celle-ci
(?m)^\h*#\h*include\h*["<](\w[\w.]*)\h*[">]
Où, (? M) est en mode multiligne,\h est un espace horizontal (alias [^\S\r\n]).
Cela marche:
import re
test_str = ' # include "header.hpp"'
match = re.match(r'\s*#\s*include\s*("[\w.]*")', test_str)
if match:
print match.group(1)