我想将一个字符串拆分成一个单词列表(这里“word”表示非空白字符的任意序列),但也保留已用作分隔符的连续空格组(因为我的数据中的空格数很重要)。对于这个简单的任务,我知道以下正则表达式可以完成这项工作(我使用Python作为说明性语言,但代码可以很容易地适应任何语言,包括正则表达式):
import re
regexA = re.compile(r"(\S+)")
print(regexA.split("aa b+b cc dd! :ee "))
产生预期的输出:
['', 'aa', ' ', 'b+b', ' ', 'cc', ' ', 'dd!', ' ', ':ee', ' ']
现在困难的部分:当一个单词包含一个左括号时,遇到的所有空格直到匹配的右括号不应被视为单词分隔符。换句话说:
regexB.split("aa b+b cc(dd! :ee (ff gg) hh) ii ")
应该产生:
['', 'aa', ' ', 'b+b', ' ', 'cc(dd! :ee (ff gg) hh)', ' ', 'ii', ' ']
使用
regexB = re.compile(r'([^(\s]*\([^)]*\)|\S+)')
适用于一对括号,但在有内括号时失败。我怎样才能改进正则表达式以正确跳过内括号?
最后一个问题:在我的数据中,只有%
开头的单词应该针对“括号规则”(regexB
)进行测试,其他单词应该由regexA
来处理。我不知道如何在一次拆分中组合两个正则表达式。
热烈欢迎任何暗示......
答案 0 :(得分:1)
在PCRE regex
引擎中,sub-routine
受支持且recursive pattern
似乎适用于包含balanced nested
括号的案例。
(?m)\s+(?=[^()]*(\([^()]*(?1)?[^()]*\))*[^()]*$)
Demo ,,,其中(?1
)表示调用子例程1,(\([^()]*(?1)?[^()]*\))
,即recursive pattern
,其中包含caller
,{{1} }
但是python不支持(?1)
中的sub-routine
模式。
所以我首先尝试将每个regex
,(
替换为另一个与众不同的角色(在此示例中为)
)并应用正则表达式进行拆分并最终将@
转回我的pythone脚本中分别有@
或(
。
分裂的正则表达式。
)
Demo ,,,其中我将您的分隔符(?m)(\s+)(?=[^@]*(?:(?:@[^@]*){2})*$)
更改为连续的空格\S+
,因为包含\s+
,@
,(
在)
'[\S]
中。
Python脚本可能是这样的
possible characters set
输出
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)
答案 1 :(得分:0)
最后,在根据@WiktorStribiż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)
以下是生成的输出:
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)', ' ']