Регулярное выражение python: как удалить все знаки препинания из строки, но оставить их между цифрами?

Я работаю над китайским проектом НЛП. Мне нужно удалить все знаки пунктуации, кроме тех, которые находятся между цифрами и остаются только китайские символы (\u4e00-\u9fff), буквенно-цифровые символы (0-9a-zA-Z). Например, дефис в 12-34 должен быть сохранен, пока знак равенства после 123 должен быть удален.

Вот мой скрипт на python.

import re
s = "中国,中,。》%国foo中¥国bar@中123=国%中国12-34中国"
res = re.sub(u'(?<=[^0-9])[^\u4e00-\u9fff0-9a-zA-Z]+(?=[^0-9])','',s)
print(res)

ожидаемый результат должен быть

中国中国foo中国bar中123国中国12-34中国

но результат

中国中国foo中国bar中123=国中国12-34中国

Я не могу понять, почему в выводе есть дополнительный знак равенства?

2 ответа

Решение

Ваше регулярное выражение сначала проверит "=" против [^\u4e00-\u9fff0-9a-zA-Z]+, Это удастся. Затем он проверит взгляд назад и вперед, которые оба должны потерпеть неудачу. Т.е.: если один из них удастся, персонаж сохраняется. Это означает, что ваш код на самом деле содержит любые не алфавитно-цифровые, не китайские символы, которые имеют цифры на любой стороне.

Вы можете попробовать следующее регулярное выражение:

u'([\u4e00-\u9fff0-9a-zA-Z]|(?<=[0-9])[^\u4e00-\u9fff0-9a-zA-Z]+(?=[0-9]))'

Вы можете использовать его как таковой:

import re
s = "中国,中,。》%国foo中¥国bar@中123=国%中国12-34中国"
res = re.findall(u'([\u4e00-\u9fff0-9a-zA-Z]|(?<=[0-9])[^\u4e00-\u9fff0-9a-zA-Z]+(?=[0-9]))',s)
print(res.join(''))

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

В Python 2 это будет выглядеть так

import re
s = u"中国,中,。》%国foo中¥国bar@中123=国%中国12-34中国"
pat_block = u'[^\u4e00-\u9fff0-9a-zA-Z]+';
pattern = u'([0-9]+{0}[0-9]+)|{0}'.format(pat_block)
res = re.sub(pattern, lambda x: x.group(1) if x.group(1) else u"" ,s)
print(res.encode("utf8")) # => 中国中国foo中国bar中123国中国12-34中国

Посмотреть демо Python

Если вам нужно сохранить эти символы внутри любых цифр Unicode, вам необходимо заменить [0-9] с \d и передать re.UNICODE флаг регулярному выражению.

Регулярное выражение будет выглядеть

([0-9]+[^\u4e00-\u9fff0-9a-zA-Z]+[0-9]+)|[^\u4e00-\u9fff0-9a-zA-Z]+

Это будет работать так:

  • ([0-9]+[^\u4e00-\u9fff0-9a-zA-Z]+[0-9]+) - захват группы 1
    • [0-9]+ - 1+ цифр
    • [^\u4e00-\u9fff0-9a-zA-Z]+ - 1+ символов, отличных от определенных в указанных диапазонах
    • [0-9]+ - 1+ цифр
  • | - или же
  • [^\u4e00-\u9fff0-9a-zA-Z]+ - 1+ символов, отличных от определенных в указанных диапазонах

В Python 2.x, когда группа не соответствует re.subобратная ссылка на него - None, поэтому лямбда-выражение требуется для проверки соответствия первой группы 1.

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