问题:我有成千上万的文档,其中包含我不想要的特定字符 . 例如 . 字符 a
. 这些文档包含各种字符,但我想要替换的是在双引号或单引号内的_1492939 .
我想找到并替换它们,我认为需要使用正则表达式 . 我正在使用VSCode,但我对任何建议持开放态度 .
我的尝试:我能够找到以下正则表达式来匹配包含 ()
内部值的特定字符串 .
".*?(r).*?"
但是,这仅突出显示整个报价 . 我想只突出这个角色 .
任何解决方案,可能在正则表达式之外,都是受欢迎的 .
示例结果:给定,字符为 a
,查找替换为 b
Somebody once told me "apples" are good for you
=> Somebody once told me "bpples" are good for you
"Aardvarks" make good kebabs
=> "Abrdvbrks" make good kebabs
The boy said "aaah!" when his mom told him he was eating aardvark
=> The boy said "bbbh!" when his mom told him he was eating aardvark
6 回答
Visual Studio代码
VS Code使用JavaScript RegEx引擎来实现其查找/替换功能 . 这意味着与其他版本(如.NET或PCRE)相比,使用正则表达式非常有限 .
幸运的是,这种味道支持前瞻和前瞻,你可以寻找但不消耗性格 . 因此,确保我们在带引号的字符串中的一种方法是在匹配
a
之后查找文件/主题字符串底部的引号数为奇数:Live demo
这会在双引号字符串中查找
a
,使单引号字符串用'
替换所有"
. 你不能同时拥有两者 .然而,上面的正则表达式存在问题,它与双引号字符串中的转义双引号冲突 . 如果重要的话要匹配它们,你还有很长的路要走:
在大文件上应用这些方法可能会导致堆栈溢出,所以让我们看看更好的方法 .
那个's great. Then I' d建议使用
awk
或sed
或更具编程性的东西来实现你所追求的目标,或者如果你能够使用Sublime Text,就有可能以更优雅的方式解决这个问题 .Sublime Text
这应该适用于具有成百上千行的大型文件,但要注意它适用于单个字符(此处为
a
),通过一些修改可能也适用于单词或子字符串:搜索:
替换为:
WHATEVER\3
Live demo
RegEx Breakdown :
三步法
最后但并非最不重要的...
匹配引号内的字符是棘手的,因为分隔符完全相同,因此在不查看相邻字符串的情况下,无法区分开关标记 . 您可以做的是将分隔符更改为其他内容,以便以后查找 .
第1步:
搜索:
"[^"\\]*(?:\\.[^"\\]*)*"
替换为:
$0Я
第2步:
搜索:
a(?=[^"\\]*(?:\\.[^"\\]*)*"Я)
用你期望的任何东西替换 .
第3步:
搜索:
"Я
没有任何东西可以替换掉所有东西 .
首先考虑以下几点:
单引号中可能有多个
a
个字符 .每个引号(使用单引号或双引号)由开头引号字符,一些文本和相同的结束引号字符组成 . 一种简单的方法是假设当引号字符按顺序计数时,奇数字符是打开引号,偶数字符是关闭引号 .
在第2点之后,可能值得进一步考虑是否应该允许单引号字符串 . 请参阅以下示例:
It's a shame 'this quoted text' isn't quoted.
这里,简单的方法会认为有两个带引号的字符串:s a shame
和isn
. 另一个:This isn't a quote ...'this is' and 'it's unclear where this quote ends'
. 我避免试图解决这些复杂问题,并采用下面的简单方法 .坏消息是第1点提出了一个问题,因为在它之后具有通配符重复字符的捕获组(例如
(.*)*
)将仅捕获最后捕获的"thing" . 但好消息是,有一种方法可以在一定范围内解决这个问题 . 许多正则表达式引擎最多允许99个捕获组(*) . 因此,如果我们可以假设每个引用中不会超过99个 UPDATE (或者即使我们不能 - 请参见步骤3),我们可以执行以下操作...(*)不幸的是我的第一个停靠点,记事本没有 - 它只允许最多9.不确定VS代码 . 但regex101(用于下面的在线演示)确实如此 .
TL; DR - 怎么办?
搜索:
"([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*([^a"]*)a*"
替换为:
"\1\2\3\4\5\6\7\8\9\10\11\12\13\14\15\16\17\18\19\20\21\22\23\24\25\26\27\28\29\30\31\32\33\34\35\36\37\38\39\40\41\42\43\44\45\46\47\48\49\50\51\52\53\54\55\56\57\58\59\60\61\62\63\64\65\66\67\68\69\70\71\72\73\74\75\76\77\78\79\80\81\82\83\84\85\86\87\88\89\90\91\92\93\94\95\96\97\98\99"
(如果已全部替换了's a possibility of > 99 such characters in a single quote until they',则可选择继续重复前两个步骤) .
重复步骤1,但在正则表达式中将所有
"
替换为'
,即:'([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*([^a']*)a*'
重复步骤2-3 .
在线演示
如果您能够将整个文本复制到“TEST STRING”的内容中,请参阅以下regex101演示,这些演示实际上可用于执行替换:
Demo for double quotes
Demo for single quotes .
使用替换模式:
据我所知,VS Code使用与JavaScript相同的正则表达式引擎,这就是我在JS中编写示例的原因 .
这样做的问题是,如果你在一组引号中有多个a,那么它将很难提取正确的值,因此需要在它背后的某种代码,或者你,锤击替换按钮直到不再找到匹配,递归模式并删除引号之间的所有a
如果您可以使用Visual Studio(而不是Visual Studio代码),则使用C和C#以及uses the .NET Framework regular expressions编写,这意味着您可以使用可变长度的lookbehind来完成此任务 .
在上面的正则表达式中添加一些逻辑,我们可以告诉它忽略前面有偶数
"
的任何位置 . 这可以防止报价之外的a
匹配 . 例如,字符串"a" a "a"
. 只匹配此字符串中的第一个和最后一个a
,但中间的一个将被忽略 .现在唯一的问题是,如果我们在两个双引号(如
"a\"" a "a"
)内转义了"
,这将会中断 . 我们需要添加更多逻辑来防止这种行为 . 幸运的是,this beautiful answer exists正确匹配转义"
. 将此逻辑添加到上面的正则表达式中,我们得到以下结果:我不确定哪种方法最适合你的字符串,但我会详细解释这个最后的正则表达式,因为它也解释了前两个 .
(?<!^[^"\n]*(?:(?:"(?:[^"\\\n]|\\.)*){2})+)
负面观察,确保前面的内容与以下内容不符^
在行首处断言位置[^"\n]*
匹配除"
或\n
之外的任何内容(?:(?:"(?:[^"\\\n]|\\.)*){2})+
匹配以下一次或多次 . 这确保了在匹配之前有任何"
它们是 balancer 的,因为有一个开始和结束的双引号 .(?:"(?:[^"\\\n]|\\.)*){2}
完全匹配以下两次"
按字面意思匹配(?:[^"\\\n]|\\.)*
匹配以下任意一次[^"\\\n]
匹配除"
,\
和\n
之外的任何内容\\.
匹配\
后跟任何字符(?<="[^"\n]*)
正向后视确保先于下列内容匹配的内容"
按字面意思匹配[^"\n]*
匹配除"
或\n
之外的任何内容a
按字面意思匹配(?=[^"\n]*")
正向前瞻确保后面的内容与以下内容相符[^"\n]*
匹配除"
或\n
之外的任何内容"
按字面意思匹配您可以从上面的模式中删除
\n
,如下所示 . 我添加它以防万一's some sort of special cases I'm不考虑(即评论)可以在你的文本中打破这个正则表达式 .\A
也强制正则表达式从字符串(或文件)的开头而不是行的开头匹配 .You can test this regex here
这就是它在Visual Studio中的样子:
I am using VSCode, but I'm open to any suggestions.
如果你想留在编辑环境中,你可以使用
Visual Studio(> = 2012)甚至记事本可以快速修复 .
这避免了必须使用虚假脚本环境 .
这两个引擎(分别是Dot-Net和boost)都使用
\G
结构 .这是在最后一个停止的位置开始下一场比赛 .
同样,这只是一个建议 .
这个正则表达式不会检查整个 balancer 报价的有效性
字符串提前(但它可以添加一行) .
这一切都是关于知道引号的内部和外部的位置 .
我评论了正则表达式,但如果你需要更多信息让我知道 .
这只是一个建议(我知道你的编辑使用ECMAScript) .
找
(?s)(?:^([^"]*(?:"[^"a]*(?=")"[^"]*(?="))*"[^"a]*)|(?!^)\G)a([^"a]*(?:(?=a.*?")|(?:"[^"]*$|"[^"]*(?=")(?:"[^"a]*(?=")"[^"]*(?="))*"[^"a]*)))
替换
$1b$2
这里的所有都是它的 .
https://regex101.com/r/loLFYH/1
评论
“内部双引号”相当棘手,因为可能会考虑复杂的场景来完全自动化 .
“引号括起来”的准确规则是什么?你需要考虑多行报价吗?您是否引用了包含转义引号或引号的字符串,而不是开始/结束字符串引用?
然而,可能有一个相当简单的表达来做你想要的大部分 .
搜索表达式:
("[^a"]*)a
替换表达式:
$1b
这不考虑引号的内部或外部 - 您可以直观地做到这一点 . 但它突出显示从引用到匹配字符的文本,因此您可以快速确定它是否在内部 .
如果您可以接受目视检查,那么我们可以构建此模式以包括不同的报价类型以及大小写 .