正则表达式 - 如何替换引号内的字符

12

你好,正则表达式专家们,

以前我从未遇到过无法用正则表达式优雅地仅用一步解决的字符串操作问题,直到现在。下面是我正在处理的示例数据:

0,"section1","(7) Delivery of 'certificate' outside the United States prohibited. Since both section 339 of the 1940 statute, 68/ and section 341 of the present law are explicit in their statement that the certificate shall be furnished the citizen, only if such individual is at the time within the United States, it is clear that the document could not and cannot be delivered outside the United States.",http://www.google.com/

1,"section2",,http://www.google.com/

2,"section3",",,",http://www.google.com/

这是一个更大的CSV文件的部分内容。我想使用一个正则表达式来替换所有出现在双引号内的逗号为下划线字符(_),不过重要的是要确保该正则表达式不会替换任何位于引号外部的逗号,因为那样会破坏CSV数据结构。

谢谢, Tom

--

澄清:

对不起,各位,我没有完全澄清我的问题,所以让我在下面概括一下:

  • 假设在引号内的引号已经被转义(在Excel保存的CSV文件中,位于引号内的引号由""或"""等表示,所以它们可以事先轻松替换)。
  • 我正在使用JavaScript。

使用上述示例文本,以下是进行正则表达式替换后应该看起来的样子(总共应该有5个替换):

0,"section1","(7) Delivery of 'certificate' outside the United States prohibited. Since both section 339 of the 1940 statute_ 68/ and section 341 of the present law are explicit in their statement that the certificate shall be furnished the citizen, only if such individual is at the time within the United States, it is clear that the document could not and cannot be delivered outside the United States.",http://www.google.com/

1,"section2",,http://www.google.com/

2,"section3","__",http://www.google.com/

证书只有在个人当前身处美国时,才能提供给公民。显然该文件无法并且无法在美国以外交付。",http://www.google.com/

1,“第2节”,,http://www.google.com/

2,“第3节”,“__”,http://www.google.com/


1
你如何在引号内部转义引号? - Assaf Lavie
@Assaf,好问题;请看我上面的澄清。 - thdoan
3个回答

21

我会帮助你,但你必须承诺停止使用“优雅”这个词。它最近工作太辛苦了,应该休息一下。:P

(?m),(?=[^"]*"(?:[^"\r\n]*"[^"]*")*[^"\r\n]*$)

如果在逗号和记录末尾之间有奇数个引号,这将匹配逗号。 我假设使用标准的CSV格式,其中记录在下一个未被引号包围的换行符处结束。 换行符可以在引号字段内部合法使用,并且如果它们被另一个引号转义,则引号也可以使用。

根据您使用的正则表达式语言的不同,您可能需要使用\r?$而不仅仅是$。例如,在.NET中,只有换行符(\n)被视为行分隔符。但是在Java中,$会在\r\n中的\r之前匹配,但不会在\r\n之间匹配(除非您设置UNIX_LINES模式)。


@Alan,我直接使用了你的正则表达式,只是省略了 ?: -- 除了记住匹配之外,还有其他用途吗? - thdoan
在许多正则表达式语言中,如果您在分割正则表达式中使用捕获组,则它们捕获的任何内容都会与常规令牌一起添加到结果中。但是我承认当时甚至没有考虑过这一点。我只是遵循一个经验法则:如果非捕获组可以完成任务,则永远不要使用捕获组。每个额外的捕获组都会增加一些资源开销,包括硬件和人员(即,对于您作为作者来说,更难跟踪哪个组捕获了什么)。 - Alan Moore
有没有办法让这个正则表达式与JavaScript的replace函数一起工作? - Allen Liu
1
@Knix: /,(?=[^"]*"(?:[^"\r\n]*"[^"]*")*[^"\r\n]*$/mg 可以解决问题。JavaScript 唯一会反对的是内联修饰符 (?m),我使用了 /m 替代它。而 "global" 修饰符 /g 则告诉它替换所有匹配项,而不仅仅是第一个。 - Alan Moore
2
@Alan,我只需要再加一个括号就可以了,它完美地工作了!谢谢!/,(?=[^"]*"(?:[^"\r\n]*"[^"]*")*[^"\r\n]*$)/mg - Allen Liu
显示剩余2条评论

3

正则表达式并不擅长匹配平衡的文本(例如起始和结束引号)。

一种天真的方法是反复应用以下内容(直到不再匹配为止):

s/(^[^"]*(?:"[^"]*"[^"]*)*?)"([^",]*),([^"]*)"/$1"$2_$3"/

但是这种方法无法处理转义引号。最好的解决方法(即最简单、最易读和最易维护的方法)是使用CSV文件解析器,逐个遍历所有字段值(在遍历过程中将逗号替换为下划线),然后将其写回文件。


0

如果您没有使用Python,以下是代码。 我没有看到您使用的任何语言指示。无论如何,我认为代码非常易懂。

import re

ch = '''0,"section1","(7) Delivery of 'certificate' outside the United States prohibited.
Since both section 339 of the 1940 statute, 68/ and section 341 of the present law are explicit
in their statement that the certificate shall be furnished the citizen, only if such individual
is at the time within the United States, it is clear that the document could not and cannot be
delivered outside the United States.",http://www.google.com/

1,"section2",,http://www.google.com/

2,"section3",",,",http://www.google.com/
'''

poto = re.compile('("[^"]+")')

def comma_replacement(match):
    return match.group().replace(',','_')

print poto.sub(comma_replacement , ch)

这种方法可以保留行中相邻的两个逗号

1,"section2",,http://www.google.com/

不变的。这是你想要的吗?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接