【发布时间】:2015-01-22 14:29:35
【问题描述】:
我正在使用带有正则表达式的 ultraedit。我想查找(并替换)和嵌入的双引号,该双引号包含在以双引号开头/结尾的字符串中。这是一个带有管道的文本文件 |作为分隔符。
如何找到嵌入的双引号:
“这个字符串没问题。”|“这是一个中间带“C”双引号的例子。”|“下一行”
我最终需要将“C”中的双引号替换为只有 C。
【问题讨论】:
我正在使用带有正则表达式的 ultraedit。我想查找(并替换)和嵌入的双引号,该双引号包含在以双引号开头/结尾的字符串中。这是一个带有管道的文本文件 |作为分隔符。
如何找到嵌入的双引号:
“这个字符串没问题。”|“这是一个中间带“C”双引号的例子。”|“下一行”
我最终需要将“C”中的双引号替换为只有 C。
【问题讨论】:
【讨论】:
你真的不想用正则表达式来做这件事。您应该使用可以理解管道分隔符的 csv 解析器。如果我只使用正则表达式,我会像这样使用多个替换:
查找并用占位符替换好的引号到文本。开始/结束报价:
s/(^"|"$)/QUOTE/g
管道分隔符附近的引号:
s/"\|"/DELIMITER/g
现在只保留嵌入的双引号。要全部删除:
s/"//g
现在把好的引号放回去:
s/QUOTE|DELIMITER/"/g
【讨论】:
试试这个发现:
(["][^"]*)["]C["]([^"]*["])
并替换:
\1C\2
在 Perl 模式下开启正则表达式。
截图
UltraEdit Professional Text/HEX Editor
Version 21.30.0.1005
开始于:
"This string is ok."|"This is example with a "C" double quoted grade in middle."|"Next line"
"This string is ok."|"This is example with a C double quoted grade in middle."|"Next line"
结尾:
"This string is ok."|"This is example with a C double quoted grade in middle."|"Next line"
"This string is ok."|"This is example with a C double quoted grade in middle."|"Next line"
第一部分。
(["][^"]*)
from (["][^"]*)["]C["]([^"]*["])
这会查找以下序列:
["]。[^"]*
["][^"]* 周围的括号表示正则表达式引擎应存储此字符序列,以便 REPLACE 部分可以引用它(作为反向引用)。第二部分。
["]C["]
from (["][^"]*)["]C["]([^"]*["])
这会查找以下序列:
["]。["]。\1C\2
\1 是一个反向引用,这意味着将其替换为保存的第一个序列。\2 是一个反向引用,这意味着将其替换为保存的第二个序列。【讨论】:
nanny 发布了一个很好的解决方案,但对于 Perl 脚本,不适用于 UltraEdit 等文本编辑器。
通常可以在字段值中使用双引号。但是每个双引号必须用另外一个双引号进行转义。例如,在有关 comma-separated values 的维基百科文章中对此进行了解释。
这种非常简单的转义算法使得在 CSV 文件中逐个字符地读取以编程语言编码的字符变得非常容易。但是双引号中包含的双引号、分隔符和换行符对于 CSV 文件中的正则表达式查找和替换来说是一场噩梦。
我在 UltraEdit 宏中记录了几个替换
InsertMode
ColumnModeOff
Top
PerlReOn
Find MatchCase RegExp "^"|"$"
Replace All "QuOtE"
Find MatchCase ""|"
Replace All "QuOtE|"
Find MatchCase "|""
Replace All "|QuOtE"
Find MatchCase """"
Replace All "QuOtEQuOtE"
Find MatchCase """
Replace All """"
Find MatchCase "QuOtE"
Replace All """
第一个替换是 Perl 正则表达式替换。行首或行尾的每个双引号都被字符串 QuOtE 替换。我很确定 CSV 文件中不存在 QuOtE。
管道字符前后的每个双引号也被 QuOtE 替换为接下来的 2 个非正则表达式替换。
CSV 文件中的转义双引号 "" 接下来被 QuOtEQuOtE 替换为非正则表达式替换。
现在将剩余的单双引号替换为两个双引号,以使其在 CSV 文件中有效。当然,您也可以删除那些单双引号。
最后,所有 QuOtE 都被替换回双引号。
注意:这不是最终的解决方案。然而,这些替换可能会产生错误的结果,例如对于像这样一个已经有效的 CSV 行
"first value with separator ""|"" included"|second value|"third value again with separator|"|fourth value contains ""Hello!"""|fifth value
结果是
"first value with separator """|""" included"|second value|"third value again with separator|"|fourth value contains ""Hello!"""|fifth value
PS:上面的有效示例行应该在电子表格应用程序中显示为
first value with separator "|" included second value third value again with separator| fourth value contains "Hello!" fifth value
【讨论】:
CSV 的最大权衡是在每种情况下都正确解析而不是简单。 这是一种适度适度的方法。如果你有真正狡猾的字符串,其中的管道旁边有引号,你最好使用像 PERL 和 Text::CSV 这样的东西。
正则表达式需要在引号的每一侧(例如[^|])都有一个非管道字符,因为解析器将吸收C,然后找不到旁边的另一个引号C.
只要您的实际 CSV 字符串中没有相邻的管道和引号,此示例就可以很好地工作。前瞻和后随是零宽度,因此它们不会删除除引号之外的任何其他字符。
1 2 3 4
(?<!^)(?<!\|)"(?!\|)(?!$)
每个匹配的引用都可以删除。不要忘记指定全局替换以获取所有引号。
【讨论】:
(?<!^)(?<!["|])"(?!["|])(?!$) 将忽略双引号。但这会导致在已经引用的值的开头或结尾处也忽略单个双引号,例如 xx|"I replied: "Thanks!""|yy。
xx|"I replied: "Thanks!""|yy 重新格式化为xx|"I replied: Thanks!"|yy,如果需要删除值中的双引号,这当然是正确的。但它也将xx|"I replied: ""Thanks!"""|yy 重新格式化为xx|"I replied: Thanks!"|yy,尽管 value 中的双引号已经用双引号正确转义,因此不应删除。通过在第一个示例中插入额外的双引号并保持已经正确格式化的第二个示例,我的宏适用于两个 CSV 行。如果目标是删除值中的所有双引号,则您的表达式效果最佳。