python 正则表达式:如何从字符串中删除所有标点符号但保留数字之间的标点符号?
python regular expression : how to remove all punctuation characters from a string but keep those between numbers?
我正在做一个中文NLP项目。我需要删除除数字之间的那些字符之外的所有标点符号,只保留汉字(\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中国
我不明白为什么输出中多了一个等号?
您的正则表达式将首先检查 "="
与 [^\u4e00-\u9fff0-9a-zA-Z]+
。这会成功。然后它将检查 lookbehind 和 lookahead,这必须 both 失败。即:如果其中之一成功,则保留该字符。这意味着您的代码实际上保留了任何非字母数字、非中文字符,这些字符在 any 一侧有数字。
您可以尝试以下正则表达式:
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(''))
我建议在数字之间匹配并捕获这些字符(以便稍后在输出中恢复它们),并在其他上下文中匹配它们。
在Python2中,会像
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中国
如果您需要在任何 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+ 个字符
在Python2.x中,当一个组在re.sub
中不匹配时,对它的反向引用是None ,这就是为什么需要 lambda 表达式来检查第 1 组是否首先匹配的原因。
我正在做一个中文NLP项目。我需要删除除数字之间的那些字符之外的所有标点符号,只保留汉字(\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中国
我不明白为什么输出中多了一个等号?
您的正则表达式将首先检查 "="
与 [^\u4e00-\u9fff0-9a-zA-Z]+
。这会成功。然后它将检查 lookbehind 和 lookahead,这必须 both 失败。即:如果其中之一成功,则保留该字符。这意味着您的代码实际上保留了任何非字母数字、非中文字符,这些字符在 any 一侧有数字。
您可以尝试以下正则表达式:
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(''))
我建议在数字之间匹配并捕获这些字符(以便稍后在输出中恢复它们),并在其他上下文中匹配它们。
在Python2中,会像
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中国
如果您需要在任何 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+ 个字符
在Python2.x中,当一个组在re.sub
中不匹配时,对它的反向引用是None ,这就是为什么需要 lambda 表达式来检查第 1 组是否首先匹配的原因。