Re.search не возвращает совпадение с (казалось бы) правильным регулярным выражением

Я пытаюсь запустить простое совпадение регулярного выражения для извлечения частей координаты dms.

Регулярное выражение кажется достаточно прочным, при копировании ниже в регулярное выражение я получаю совпадение:

regex: ^([0-9]{1,2})[:|°| ]([0-9]{1,2})[:|'|′| ]?([0-9]{1,4}(?:\.[0-9]+){0,1})?[\"|″| ]([N|S])$
body: 51°30'30.7080"N

В моем скрипте python мне нужно следующее:

# coding: utf8
import re
dms_regex = "^([0-9]{1,2})[:|°| ]([0-9]{1,2})[:|'|′| ]?([0-9]{1,4}(?:\.[0-9]+){0,1})?[\"|″| ]([N|S])$"

def dmsToCoordinates(dms_lat):
    print(dms_lat)
    matches = re.search(dms_regex, dms_lat)
    print(matches)

dmsToCoordinates("51°30'30.7080\"N")

Мой вывод на терминал:

51°30'30.7080"N
None

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

Наверное, где-то есть какие-то невидимые персонажи. Попробуйте добавить \W* после ^ и перед $. Также удалите избыточные |, см. regex101.com/r/jB3370/1

Wiktor Stribiżew 31.10.2018 11:39

На выходе я получаю <re.Match object; span=(0, 15), match='51°30\'30.7080"N'>.

Aran-Fey 31.10.2018 11:39

Не уверен, стоит ли добавлять, но у меня установлена ​​кодировка utf-8 (# -- кодировка: utf-8 -- это первая строка моего файла)

Preston 31.10.2018 11:41

Вы скопировали откуда-то этот строковый литерал? Или набрать от руки? Я подозреваю, что там не печатаемые символы.

Andras Deak 31.10.2018 11:46

@AndrasDeak скопировано из Google

Preston 31.10.2018 11:47

@downvoter заботиться об объяснении?

Preston 31.10.2018 11:49

Вы можете проверить from unicodedata import name; for c in dms_lat: print(name(c)) и найти виновника.

Andras Deak 31.10.2018 11:49

И, конечно же, введите чистую строку вручную, поскольку ваш код работает.

Andras Deak 31.10.2018 11:53

@AndrasDeak попробовал ввести символы в шаблоне и строке регулярного выражения, но результат тот же. Цикл for дает мне TypeError: name() argument 1 must be unicode, not str (спасибо за помощь)

Preston 31.10.2018 12:19

Похоже на питон 2 ....

Andras Deak 31.10.2018 13:57

@AndrasDeak по ошибке запустил скрипт из моего venv, yikes: s. Это отлично работает в python 3, если вы хотите добавить ответ на этот конец, очки ваши

Preston 31.10.2018 14:15

@ User632716 спасибо, я действительно решил добавить ответ, потому что даже зная, что это произошло из-за неправильной версии python, я обнаружил, что проблема сбивает с толку.

Andras Deak 31.10.2018 22:51
0
12
325
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Как выяснилось (в основном случайно) в ветке комментариев по вопросу: вы по ошибке использовали неправильную (старую) версию python.

Одна из основных проблем (если не основная проблема в) с python 2 заключалась в том, что обработка строк была полностью нарушена. Тип str был байтовой строкой с отдельным типом Unicode для текста Unicode. Это собрало воедино текст и данные.

Итак, когда вы ввели символ, отличный от ascii (например, знак градуса), и запустили свой код на python 2, произошло следующее:

>>> '°'
'\xc2\xb0'
>>> len('°')
2
>>> '°'.decode('utf8')
u'\xb0'
>>> len('°'.decode('utf8'))
1

Строковый литерал '°' (байт) становится двумя байтами как данные, но все еще маскируется под строку! Это только правильная односимвольная строка, если вы ее закодируете и получите строку в Юникоде. Итак, когда вы помещаете его в класс персонажа:

>>> 'f[°]oo'
'f[\xc2\xb0]oo'

два байта будут действовать как два символа в классе символов: один '\xc2', другой '\xb0'. Это означает, что он не будет соответствовать байтам '\xc2\xb0', вставленным вместо другого буквенного знака степени в целевой строке:

>>> re.search('f[°]oo', 'f°oo') is None
True
>>> re.search('f[\xc2\xb0]oo', 'f\xc2\xb0oo') is None # exact same thing as previous
True
>>> re.search('f[\xc2\xb0]oo', 'f\xc2oo') is None
False

Таким образом, ваше случайное использование python 2 привело к тому, что регулярное выражение сломалось на символах, отличных от ascii, что в основном связано с тем, как строки разбиты в python 2. Если бы кто-то использовал код в python 2, им, по крайней мере, пришлось бы переключиться на Литералы Unicode u'' как в шаблоне, так и в цели, что волшебным образом решит проблему.

И два общих замечания о вашем регулярном выражении:

  1. вы должны удалить все эти трубы из классов персонажей. Классы символов означают «использовать персонажа из этого набора символов», что означает, что

    (a) каналы не нужны для принудительного выбора между символами в классе символов, и

    (б) он будет соответствовать реальным каналам в цели: re.search('[a|b]','|') is not None

  2. вы должны использовать строковые литералы сырой для представления регулярных выражений, иначе вам иногда придется избегать обратных косых черт в escape-последовательностях, чтобы получить правильное соответствие и предотвратить двусмысленность.

Поэтому я предлагаю вместо этого такую ​​схему:

dms_regex = r"^([0-9]{1,2})[:° ]([0-9]{1,2})[:'′ ]?([0-9]{1,4}(?:\.[0-9]+){0,1})?[\"″ ]([NS])$"

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