Python 如何在使用正则表达式将行拆分为单词时转义特定的空格
我想将一个字符串拆分为一个单词列表(这里的“word”表示非空白字符的任意序列),但同时保留已用作分隔符的连续空白组(因为空白的数量在我的数据中很重要)。对于这个简单的任务,我知道下面的正则表达式可以完成这项工作(我使用Python作为说明性语言,但是代码可以很容易地适应任何语言,包括正则表达式): 生成预期输出:Python 如何在使用正则表达式将行拆分为单词时转义特定的空格,python,regex,Python,Regex,我想将一个字符串拆分为一个单词列表(这里的“word”表示非空白字符的任意序列),但同时保留已用作分隔符的连续空白组(因为空白的数量在我的数据中很重要)。对于这个简单的任务,我知道下面的正则表达式可以完成这项工作(我使用Python作为说明性语言,但是代码可以很容易地适应任何语言,包括正则表达式): 生成预期输出: ['', 'aa', ' ', 'b+b', ' ', 'cc', ' ', 'dd!', ' ', ':ee', ' '] Solution 1: ['', 'aa'
['', 'aa', ' ', 'b+b', ' ', 'cc', ' ', 'dd!', ' ', ':ee', ' ']
Solution 1: ['', 'aa', ' ', 'b%b(', ' ', '%cc(dd!', ' ', '(:ee', ' ', 'ff)', ' ', 'gg)', ' ', '%hh', ' ', 'ii)', ' ']
Solution 2: ['', 'aa', ' ', 'b%b(', ' ', '%cc(dd! (:ee ff)', ' ', 'gg)', ' ', '%hh', ' ', 'ii)', ' ']
Solution 3: ['', 'aa', ' ', 'b%b(', ' ', '%cc(dd! (:ee ff) gg)', ' ', '%hh', ' ', 'ii)', ' ']
Solution 4: ['', 'aa', ' ', 'b%b(', ' ', '%cc(dd! (:ee ff) gg)', ' ', '%hh', ' ', 'ii)', ' ']
现在最难的部分是:当一个单词包含一个左括号时,在匹配的右括号之前遇到的所有空格都不应被视为单词分隔符。换言之:
regexB.split("aa b+b cc(dd! :ee (ff gg) hh) ii ")
应产生:
['', 'aa', ' ', 'b+b', ' ', 'cc(dd! :ee (ff gg) hh)', ' ', 'ii', ' ']
使用
适用于一对括号,但在有内括号时失败。如何改进正则表达式以正确跳过内括号
最后一个问题:在我的数据中,只有以%
开头的单词应该被测试为“括号规则”(regexB
),其他单词应该被regexA
处理。我不知道如何在一次拆分中合并两个正则表达式
欢迎任何提示…在
PCRE regex
引擎中,支持子程序
,并且递归模式
似乎适用于包含平衡嵌套
括号的情况
(?m)\s+(?=[^()]*(\([^()]*(?1)?[^()]*\))*[^()]*$)
,其中(?1
)表示调用子程序1,(\([^()]*(?1)?[^()]*\)
,即递归模式
,其中包括调用方
,(?1)
但是python不支持regex
中的子例程
模式
因此,我首先尝试将每个(
,)
替换为另一个不同的字符(@
,在本例中)并应用正则表达式进行拆分,最后在pythone脚本中分别将@
返回到(
或)
用于拆分的正则表达式
(?m)(\s+)(?=[^@]*(?:(?:@[^@]*){2})*$)
,其中我将分隔符\S+
更改为连续空格\S+
,因为@
,(
,)
包含在[\S]
可能的字符集中
Python脚本可能是这样的
import re
ss="""aa b+b cc(dd! :ee ((ff gg)) hh) ii """
ss=re.sub(r"\(|\)","@",ss) #repacing every `(`,`)` to `@`
regx=re.compile(r"(?m)(\s+)(?=[^@]*(?:(?:@[^@]*){2})*$)")
m=regx.split(ss)
for i in range(len(m)): # turn `@` back to `(` or `)` respectively
n= m[i].count('@')
if n < 2: continue
else:
for j in range(int(n/2)):
k=m[i].find('@'); m[i]=m[i][:k]+'('+m[i][k+1:]
m[i]= m[i].replace("@",')')
print(m)
最后,在基于@Wiktor Stribiżew和@Thm Lee提出的答案测试了几个想法之后,我找到了一系列处理不同复杂程度的解决方案。为了减少依赖性,我想继续使用Python标准库中的
re
模块,下面是代码:
import re
text = "aa b%b( %cc(dd! (:ee ff) gg) %hh ii) "
# Solution 1: don't process parentheses at all
regexA = re.compile(r'(\S+)')
print(regexA.split(text))
# Solution 2: works for non-nested parentheses
regexB = re.compile(r'(%[^(\s]*\([^)]*\)|\S+)')
print(regexB.split(text))
# Solution 3: works for one level of nested parentheses
regexC = re.compile(r'(%[^(\s]*\((?:[^()]*\([^)]*\))*[^)]*\)|\S+)')
print(regexC.split(text))
# Solution 4: works for arbitrary levels of nested parentheses
n, words = 0, []
for word in regexA.split(text):
if n: words[-1] += word
else: words.append(word)
if n or (word and word[0] == '%'):
n += word.count('(') - word.count(')')
print(words)
以下是生成的输出:
['', 'aa', ' ', 'b+b', ' ', 'cc', ' ', 'dd!', ' ', ':ee', ' ']
Solution 1: ['', 'aa', ' ', 'b%b(', ' ', '%cc(dd!', ' ', '(:ee', ' ', 'ff)', ' ', 'gg)', ' ', '%hh', ' ', 'ii)', ' ']
Solution 2: ['', 'aa', ' ', 'b%b(', ' ', '%cc(dd! (:ee ff)', ' ', 'gg)', ' ', '%hh', ' ', 'ii)', ' ']
Solution 3: ['', 'aa', ' ', 'b%b(', ' ', '%cc(dd! (:ee ff) gg)', ' ', '%hh', ' ', 'ii)', ' ']
Solution 4: ['', 'aa', ' ', 'b%b(', ' ', '%cc(dd! (:ee ff) gg)', ' ', '%hh', ' ', 'ii)', ' ']
如OP中所述,对于我的特定数据,只需对以%
开头的单词和其他括号(例如单词b%b)(
在我的示例中)进行括号中的空格转义不认为是特殊的。如果要转义任何一对括号内的空格,只需删除正则表达式中的%
字符即可。下面是该修改的结果:
Solution 1: ['', 'aa', ' ', 'b%b(', ' ', '%cc(dd!', ' ', '(:ee', ' ', 'ff)', ' ', 'gg)', ' ', '%hh', ' ', 'ii)', ' ']
Solution 2: ['', 'aa', ' ', 'b%b( %cc(dd! (:ee ff)', ' ', 'gg)', ' ', '%hh', ' ', 'ii)', ' ']
Solution 3: ['', 'aa', ' ', 'b%b( %cc(dd! (:ee ff) gg)', ' ', '%hh', ' ', 'ii)', ' ']
Solution 4: ['', 'aa', ' ', 'b%b( %cc(dd! (:ee ff) gg) %hh ii)', ' ']
正则表达式无法匹配括号之类的嵌套结构。您必须编写一些代码。@Aran:我同意一般情况,但在我的情况下,我知道最多有一对内括号。这个约束是否会改变pb?正如Aran Fey所说,正则表达式无法理解嵌套。例如,对于
(a b(c d)e f)之类的字符串
,如果您的正则表达式是非贪婪的,那么它将匹配(ab(cd)
和(cd)
。如果它是贪婪的,它将匹配(ab(cd)ef)
和(cd)ef)
。这两种方法都有不同的问题。但是,正如此答案中所解释的,Python确实有您可能需要研究的解析库:。阅读您的答案后,我花了一些时间才理解子程序的功能(可惜标准的re
模块不支持它们).但我喜欢你的替代方法,它适用于标准的re
模块。我只是稍微扭曲了一下,加入了@Wiktor的finditer
思想,以获得更短更干净的代码。谢谢你的信息。Wiktor的方法很棒:-)
Solution 1: ['', 'aa', ' ', 'b%b(', ' ', '%cc(dd!', ' ', '(:ee', ' ', 'ff)', ' ', 'gg)', ' ', '%hh', ' ', 'ii)', ' ']
Solution 2: ['', 'aa', ' ', 'b%b( %cc(dd! (:ee ff)', ' ', 'gg)', ' ', '%hh', ' ', 'ii)', ' ']
Solution 3: ['', 'aa', ' ', 'b%b( %cc(dd! (:ee ff) gg)', ' ', '%hh', ' ', 'ii)', ' ']
Solution 4: ['', 'aa', ' ', 'b%b( %cc(dd! (:ee ff) gg) %hh ii)', ' ']