正则表达式替换:在匹配中进行多个替换

6
我正在将我们的MVC3项目转换为使用T4MVC。我希望能够将JavaScript包含替换为与T4MVC一起使用。因此,我需要替换


"~/Scripts/DataTables/TableTools/TableTools.min.js"
"~/Scripts/jquery-ui-1.8.24.min.js"

进入

Scripts.DataTables.TableTools.TableTools_min_js
Scripts.jquery_ui_1_8_24_min_js

我目前使用Notepad++作为正则表达式工具,它使用POSIX正则表达式。 我可以使用以下正则表达式查找脚本名称并替换:
查找: \("~/Scripts/(.*)"\) 替换为: \(Scripts.\1\) 但我无法想出如何将文件名中的点和破折号替换为下划线,并将正斜杠替换为点。 我可以使用以下方法检查js文件名称是否包含点或破折号:
 \("~/Scripts/(?=\.*)(?=\-*).*"\)

但是如何替换组内的组?
需要在组内进行非贪婪替换,并按顺序进行这些替换,因此将正斜杠转换为点后就不会再转换为下划线。
这是一个非关键性问题,我已经手动完成了所有替换,但我认为自己很擅长正则表达式,所以这个问题让我感到困扰! p.s. 首选工具是Notepad ++,但任何POSIX正则表达式解决方案都可以使用 -) p.p.s. 在这里你可以得到一个样本要被替换的东西 这里是目标文本

你的目标是将一些文本复制到编辑器中,让它进行替换,然后再复制回来吗? - Nick
是的,基本上就是这样。从Visual Studio中复制一些内容,进行替换,然后再复制回VS。我确定VS无法处理这个,所以必须在其他地方完成。 - trailmax
1
不确定您使用的是哪个版本的VS,但PowerGUI控制台可让您在Visual Studio中访问PowerShell以操纵编辑器环境,因此您可以在VS中方便地获得漂亮的正则表达式查找。也许值得一试。 - Thell
哦,不错!我会试一下的!我正在使用VS2012。内置的正则表达式搜索替换非常奇怪且不兼容。 - trailmax
4个回答

3
如果你想使用N++,那么请使用N++ Python Script。设置脚本并分配快捷键,然后你就可以使用单一的通行方案,只需要打开、修改和保存...再也没有比这更简单的了。
我认为问题的部分原因是N++不是一个正则表达式工具,有时候需要使用专门的正则表达式工具或者搜索/替换方案。在速度和时间价值方面,使用专门用于文本处理而非编辑的工具可能更好。
[脚本编辑]:已经修改以匹配修改后的输入/输出期望。
# Substitute & Replace within matched group.
from Npp import *
import re

def repl(m):
    return "(Scripts." + re.sub( "[-.]", "_", m.group(1) ).replace( "/", "." ) + ")"

editor.pyreplace( '(?:[(].*?Scripts.)(.*?)(?:"?[)])',  repl )
  1. 安装:插件 -> 插件管理器 -> Python脚本
  2. 新建脚本:插件 -> Python脚本 -> 脚本名称.py
  3. 选择目标选项卡。
  4. 运行:插件 -> Python脚本 -> 脚本 -> 脚本名称

[编辑:一个扩展的一行PythonScript命令]

由于需要新的Python正则表达式模块(我希望它可以取代re),我进行了测试并为N ++ PythonScript插件编译了该模块,并决定在您的示例集上对其进行测试。

控制台上的两个命令以正确的结果出现在编辑器中。

import regex as re
editor.setText( (re.compile( r'(?<=.*Content[(].*)((?<omit>["~]+?([~])[/]|["])|(?<toUnderscore>[-.]+)|(?<toDot>[/]+))+(?=.*[)]".*)' ) ).sub(lambda m: {'omit':'','toDot':'.','toUnderscore':'_'}[[ key for key, value in m.groupdict().items() if value != None ][0]], editor.getText() ) )

非常甜美!

使用regex而不是re的另一个很酷的地方是,我能够在Expresso中构建表达式并直接使用它!这允许通过将r''字符串部分复制粘贴到Expresso中进行详细说明。

其中缩写文本为:

Match a prefix but exclude it from the capture. [.*Content[(].*]
[1]: A numbered capture group. [(?<omit>["~]+?([~])[/]|["])|(?<toUnderscore>[-.]+)|(?<toDot>[/]+)], one or more repetitions
    Select from 3 alternatives
         [omit]: A named capture group. [["~]+?([~])[/]|["]]
             Select from 2 alternatives
                 ["~]+?([~])[/]
                 Any character in this class: ["]
         [toUnderscore]: A named capture group. [[-.]+]
         [toDot]: A named capture group. [[/]+]
Match a suffix but exclude it from the capture. [.*[)]".*]

命令分解非常巧妙,我们正在告诉Scintilla将完整的缓冲区内容设置为编译后的正则表达式替换命令的结果,基本上是通过使用名称不为空的组的“开关”来实现的。
希望PythonScript作者Dave将正则表达式模块添加到该项目的ExtraPythonLibs部分。

是的... N++ Python脚本很棒! - Thell

3
这里是一个简单的Notepad++解决方案,但并不是最优雅的解决方案。我通过对文件进行几次转换来完成这个过程。
第一遍
用下划线替换点和破折号。
查找:("~/Scripts[^"]*?)[.-] 替换为:\1_ 不幸的是,我没有找到一种只匹配点或破折号的方法,因为这需要后顾,而Notepad++显然不支持后顾。由于这个原因,每次你执行替换时,一个脚本名中的第一个点或破折号将被替换(因为匹配不能重叠)。因此,你必须多次运行此替换,直到不再进行任何替换(在你的示例输入中,这将是8次)。
第二遍
用点替换斜杠。
查找:("~/Scripts[^"]*?)/ 替换为:\1. 这基本上与第一步相同,只是使用了不同的字符(对于示例文件,您需要这样做3次)。按照这个顺序进行处理可以确保没有斜杠会变成下划线。
第三遍
去掉周围的字符。
查找:"~/(Scripts[^"]*?)" 替换为:\1 现在,这将匹配所有仍然被"~/"包围的脚本名称,并捕获中间的内容并输出它。
请注意,在第一和第二步的查找模式中包含这些周围字符,可以避免转换已经是新格式字符串的.
正如我所说,这不是最方便的方法。特别是,因为必须手动多次执行第一步和第二步。但是对于大型文件来说,仍然可以节省很多时间,而且我无法想出一种在没有后顾功能的情况下,以一次操作得到所有正确字符串的方法。当然,我非常欢迎改进此解决方案的建议。希望我至少能为你(和任何有类似问题的人)提供一个起点。

不错的努力!谢谢。我希望避免多次处理,至少使用相同的正则表达式。但是由于缺乏响应,似乎这是不可能的。 - trailmax
如果您能够提供一个支持正向、可变长度回溯的 POSIX 正则表达式引擎的名称,我可以尝试找到一种解决方案,每次只使用一次通过(即使这样,我也不确定回溯重叠是否被允许)。 - Martin Ender
是的,我没有意识到Notepad++不支持所有所需的正则表达式功能。但正如Nick所说,RegexHero可以执行所有前瞻和后顾操作。它还允许在两个步骤中进行转换。 - trailmax
1
是的,那就是我想到的反向预查解决方案。我只是不知道有一个可用于支持它们的引擎:)。我也非常喜欢Python脚本的解决方案...尽管它需要一个额外的插件。 - Martin Ender

3
我建议使用类似于RegexHero的网站。
  1. 将代码粘贴到目标字符串框中,然后在“正则表达式”框中放置(?<=(~/Script).*)[.-](?=(.*"[)]")),并在替换字符串框中放置_

  2. 完成替换后,在底部单击最终字符串,然后选择移动到目标字符串并开始新表达式

  3. 从那里,将(?<=(<script).*)("~/)(?=(.*[)]" ))|(?<=(Url.).*)(")(?=(.*(\)" )))粘贴到“正则表达式”框中,并将替换字符串框留空。

  4. 完成替换后,在底部单击最终字符串,然后选择移动到目标字符串并开始新表达式

  5. 从那里将(?<=(Script).*)[/](?=(.*[)]"))粘贴到“正则表达式”框中,并将.放入替换字符串框中。

完成后,最终字符串框中将显示您要查找的内容。我不确定可以解析多少文本,但如果有问题,可以将其拆分。我相信可能有更好的方法来完成这个任务,但这是我处理此类事情的方式之一。我喜欢这个网站的原因之一是因为我不需要安装任何东西,所以可以在任何地方快速完成。
编辑1:根据评论,我将步骤3移动到步骤5,并添加了新的步骤3和4。我不得不这样做,因为新的步骤5会用.替换"~/Scripts中的/,从而破坏了去除"~/。我还不得不更改步骤5的代码以考虑Script的起始位置的更改。

绝对的魔法!谢谢提供这个链接——我以前从未见过,但我能看出它将来会经常使用!而且这个解决方案目前为止是最简单的,只需要 2 次通过。赢家就是它了! - trailmax
1
@trailmax 在你的问题中,我有什么地方理解错了吗?你在问题中提供的示例行与此解决方案呈现的输出不匹配。也许你可以编辑你的问题示例,提供完整的行? - Thell
@Thell 哦!你说得对。我确实漏了引号和 Scripts 前面的波浪号。这不是什么大问题,可以进行替换。但是需要使用正则表达式再进行一次操作,这样就需要三次操作了。 - trailmax
1
@trailmax 我已经调整了步骤,以考虑到 "~/ 和最后一个 " - Nick
这不是一个非常通用的解决方案,因为它只适用于Windows或IE 9+(Silverlight)。无法为Mac或Linux用户工作。(尽管对于这个特定的问题来说,也许这没关系,因为OP明确表示他们正在使用仅限Windows的Notepad ++。) - Sean the Bean
显示剩余2条评论

2

或者你可以使用一个脚本来完成这个任务,避免复制粘贴和其余的手动操作。考虑使用以下脚本:

$_.gsub!(%r{(?:"~/)?Scripts/([a-z0-9./-]+)"?}i) do |i| 
    'Scripts.' + $1.split('/').map { |i| i.gsub(/[.-]/, '_') }.join('.')
end

这样运行它:

$ ruby -pi.bak script.rb *.ext

所有扩展名为.ext的文件将被就地编辑,并将原始文件保存为.ext.bak扩展名。如果您使用版本控制(而且您应该这样做),那么您可以使用一些可视化差异工具轻松地查看更改,如有必要,则进行更正并在提交之后进行提交。


这只是一个文件中的一次修改,有点过度了。但是因为引入了新工具,所以加1分。不幸的是,我无法测试——没有设置任何Ruby环境。 - trailmax
1
当然。我认为学习使用像Ruby、Python或Perl这样的工具来完成这样的任务是很好的选择。即使是像这样的一次性任务,你也会比使用编辑器更快。编辑器通常只提供简单的正则表达式替换,而任何稍微不同的操作都会变成问题。而使用像这个脚本这样的脚本,只需要一步就可以实现对多个文件或其他重复场景的自动化。 - detunized
是的,我在完全转向Windows之前使用过Bash。我本来打算研究一段时间的Ruby - 现在可能会开始了 -) - trailmax

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