Регулярное выражение, чтобы подтвердить, является ли строка допустимым идентификатором в Python - PullRequest
14 голосов
/ 29 марта 2011

У меня есть следующее определение для идентификатора:

Identifier --> letter{ letter| digit}

По сути, у меня есть функция идентификатора, которая получает строку из файла и проверяет ее, чтобы убедиться, что это действительный идентификатор, как определено выше.

Я пробовал это:

if re.match('\w+(\w\d)?', i):     
  return True
else:
  return False

, но когда я запускаю свою программу каждый раз, когда она встречает целое число, она думает, что это действительный идентификатор.

Например

c = 0 ;

он печатает c в качестве действительного идентификатора, что хорошо, но он также печатает 0 в качестве действительного идентификатора.

Что я здесь не так делаю?

Ответы [ 5 ]

22 голосов
/ 13 апреля 2012

Из официальная ссылка : identifier ::= (letter|"_") (letter | digit | "_")*

Таким образом, регулярное выражение:

^[^\d\W]\w*\Z

Пример (для Python 2 просто опустите re.UNICODE):

import re
identifier = re.compile(r"^[^\d\W]\w*\Z", re.UNICODE)

tests = [ "a", "a1", "_a1", "1a", "aa$%@%", "aa bb", "aa_bb", "aa\n" ]
for test in tests:
    result = re.match(identifier, test)
    print("%r\t= %s" % (test, (result is not None)))

Результат:

'a' = True
'a1'    = True
'_a1'   = True
'1a'    = False
'aa$%@%'    = False
'aa bb' = False
'aa_bb' = True
'aa\n'  = False
4 голосов
/ 06 января 2019

str.isidentifier() работает. Ответы регулярного выражения неправильно не совпадают с некоторыми действительными идентификаторами Python и неправильно совпадают с некоторыми недействительными.

str.isidentifier() Возвращает true, если строка является допустимым идентификатором в соответствии с определением языка, раздел Идентификаторы и ключевые слова.

Используйте keyword.iskeyword() для проверки зарезервированных идентификаторов, таких как def и класс.

@ Комментарий Мартино приводит пример '℘᧚', когда решения регулярных выражений терпят неудачу.

>>> '℘᧚'.isidentifier()
True
>>> import re
>>> bool(re.search(r'^[^\d\W]\w*\Z', '℘᧚'))
False

Почему это происходит?

Позволяет определить наборы кодовых точек, которые соответствуют заданному регулярному выражению, и набор, который соответствует str.isidentifier.

import re
import unicodedata

chars = {chr(i) for i in range(0x10ffff) if re.fullmatch(r'^[^\d\W]\w*\Z', chr(i))}
identifiers = {chr(i) for i in range(0x10ffff) if chr(i).isidentifier()}

Сколько совпадений с регулярным выражением не являются идентификаторами?

In [26]: len(chars - identifiers)                                                                                                               
Out[26]: 698

Сколько идентификаторов не совпадают с регулярными выражениями?

In [27]: len(identifiers - chars)                                                                                                               
Out[27]: 4

Интересно - какие?

In [37]: {(c, unicodedata.name(c), unicodedata.category(c)) for c in identifiers - chars}                                                       
Out[37]: 
set([
    ('\u1885', 'MONGOLIAN LETTER ALI GALI BALUDA', 'Mn'),
    ('\u1886', 'MONGOLIAN LETTER ALI GALI THREE BALUDA', 'Mn'),
    ('℘', 'SCRIPT CAPITAL P', 'Sm'),
    ('℮', 'ESTIMATED SYMBOL', 'So'),
])

Чем отличаются эти два набора?

Они имеют разные значения Unicode "General Category".

In [31]: {unicodedata.category(c) for c in chars - identifiers}                                                                                 
Out[31]: set(['Lm', 'Lo', 'No'])

Из Википедии , это Letter, modifier; Letter, other; Number, other. Это согласуется с re docs , поскольку \d - это только десятичные цифры:

\d Соответствует любой десятичной цифре Юникода (то есть любому символу в категории символов Юникода [Nd])

А как же иначе?

In [32]: {unicodedata.category(c) for c in identifiers - chars}                                                                                 
Out[32]: set(['Mn', 'Sm', 'So'])

Это Mark, nonspacing; Symbol, math; Symbol, other.

Где все это задокументировано?

Где это реализовано?

https://github.com/python/cpython/commit/47383403a0a11259acb640406a8efc38981d2255

Я все еще хочу регулярное выражение

Посмотрите на модуль regex в PyPI.

Эта реализация регулярного выражения обратно совместима со стандартным модулем 're', но предлагает дополнительные функции.

Включает фильтры для "общей категории".

3 голосов
/ 29 марта 2011

Для Python 3 вам нужно обрабатывать буквы и цифры Unicode. Так что, если это проблема, вы должны ладить с этим:

re_ident = re.compile(r"^[^\d\W]\w*$", re.UNICODE)

[^\d\W] соответствует символу, который не является цифрой и не «не алфавитно-цифровым», что переводится как «символ, который является буквой или подчеркиванием».

2 голосов
/ 29 марта 2011

\ w соответствует цифрам и символам. Попробуйте ^[_a-zA-Z]\w*$

0 голосов
/ 27 декабря 2018

Работает как шарм: r'[^\d\W][\w\d]+'

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...