使用Python按分隔符分割字符串,同时忽略分隔符并转义引号

2 投票
3 回答
3039 浏览
提问于 2025-04-16 12:43

我正在尝试根据分隔符的位置来拆分一个字符串(我想从Fortran代码中去掉注释)。我可以在下面的字符串中使用!来进行拆分:

x = '''print "hi!" ! Remove me'''
pattern = '''(?:[^!"]|"[^"]*")+'''
y = re.search(pattern, x)

但是,如果字符串中包含转义引号,这样做就会失败,比如:

z = '''print "h\"i!" ! Remove me'''

那么,正则表达式能不能修改一下来处理转义引号呢?还是说我根本就不应该用正则表达式来解决这种问题?

3 个回答

2

Fortran 语言的解析其实挺复杂的(比如可以看看这个讨论 这里)。我对语法的细节并不太了解,也不知道 '!' 这个符号可能出现在什么地方。所以我有个想法:评论里出现 '!' 的可能性有多大呢?如果这种可能性不大,你可以考虑在每一行中把最后一个 '!' 后面的内容都删掉:

def cleanup(line):
  splitlist = line.split("!")
  if len(splitlist) > 1 and "\"" not in splitlist[-1]:
      return '!'.join(splitlist[:-1]).strip()
  else:
      return line

这样做虽然不完美,但最坏的情况也就是留下些不完整的评论。这不会影响到实际的代码。

补充:

看起来 NumPy 包里有一个基于 Python 的 Fortran 解析器,叫做 F2py。根据许可的限制,你可能可以对它进行修改,以便可靠地解析“代码而不是评论”。

2

你需要的是一种叫做负向前瞻的东西,写作 (?<!...)

举个例子:

z = r'''print "h\"i!" ! Remove me'''
pattern = r'''(?:[^!"]|(?<!\\)".*(?<!\\)")+'''
y = re.search(pattern, z)

print(y.group(0))


输出结果:

print "h\"i!" 



正如评论中提到的,上面的表达式无法处理转义的反斜杠。此外,它也不支持FORTRAN中允许使用的单引号。这个表达式应该能处理这些情况(我觉得):

 pattern = r'''(?:[^!"']|((?<!\\)"|(\\\\)+").*?((?<!\\)"|(\\\\)+")|((?<!\\)'|(\\\\)+').*?((?<!\\)"|(\\\\)+'))+'''

这有点复杂了……

3

这里有一个经过验证的正则表达式(来自《正则表达式精通指南》),可以用来匹配包含双引号的字符串,这些字符串可能会有用反斜杠转义的引号:

r'"[^"\\]*(?:\\.[^"\\]*)*"'

在这对引号之间,它会处理任何以反斜杠开头的字符对,而不需要去识别第二个字符;这让它可以轻松处理转义的反斜杠和其他转义序列,不会增加额外的麻烦。而且在没有占有量词原子组的情况下,它的效率也是非常高的,因为这些在Python中是不支持的。

你应用的完整正则表达式是:

r'^((?:[^!"]+|"[^"\\]*(?:\\.[^"\\]*)*")*)!.*$'

这个表达式只匹配包含注释的行,并且会在第1组中捕获注释之前的所有内容。对于以!开头的行,捕获的内容可能是零长度的。这个正则表达式是为了和sub一起使用,而不是search,如下所示:

import re

pattern = r'^((?:[^!"]+|"[^"\\]*(?:\\.[^"\\]*)*")*)!.*$'

x = '''print "hi!" ! Remove me'''
y = re.sub(pattern, r'\1', x)
print(y)

在ideone.com上查看实际效果

免责声明:这个回答不是关于FORTRAN的,只是关于遵循问题中规定的规则的代码。我从来没有使用过FORTRAN,而且在过去一个小时里找到的每个参考资料似乎都在描述一种完全不同的语言。唉!

撰写回答