Python 如何在使用正则表达式将行拆分为单词时转义特定的空格

Python 如何在使用正则表达式将行拆分为单词时转义特定的空格,python,regex,Python,Regex,我想将一个字符串拆分为一个单词列表(这里的“word”表示非空白字符的任意序列),但同时保留已用作分隔符的连续空白组(因为空白的数量在我的数据中很重要)。对于这个简单的任务,我知道下面的正则表达式可以完成这项工作(我使用Python作为说明性语言,但是代码可以很容易地适应任何语言,包括正则表达式): 生成预期输出: ['', 'aa', ' ', 'b+b', ' ', 'cc', ' ', 'dd!', ' ', ':ee', ' '] Solution 1: ['', 'aa'

我想将一个字符串拆分为一个单词列表(这里的“word”表示非空白字符的任意序列),但同时保留已用作分隔符的连续空白组(因为空白的数量在我的数据中很重要)。对于这个简单的任务,我知道下面的正则表达式可以完成这项工作(我使用Python作为说明性语言,但是代码可以很容易地适应任何语言,包括正则表达式):

生成预期输出:

['', '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)', '  ']