【问题标题】:How can I search for two sequential newlines (\n) using perl?如何使用 perl 搜索两个连续的换行符 (\n)?
【发布时间】:2012-01-31 15:20:29
【问题描述】:

如何使用 perl 搜索两个连续的换行符 (\n)?或者更具体地说,为什么对 \n\n 的搜索即使存在也不成功?我有一个带有连续换行符的文件(用十六进制编辑器验证,它们不是返回等),但 perl 似乎不接受这个正则表达式。

perl -pi -e 's/\n\n/TEST/g' myfile.xml = 没有结果

我实际上是在尝试在 XML 文件中插入一些代码,但换行符在中间,这样做最优雅的方式是什么?我想出了一个可怕的 perl 单行,但双换行似乎导致了失败。

我想从以下位置更改 gtkrc 文件的一部分:

GtkWidget::link-color = @link_color
GtkWidget::visited-link-color = @text_color

####################
# Color Definitions
####################

到:

GtkWidget::link-color = @link_color
GtkWidget::visited-link-color = @text_color

GtkWindow::resize-grip-height = 0
GtkWindow::resize-grip-width = 0

####################
# Color Definitions
####################

使用原始代码的一部分作为我的搜索词(在原始脚本的每一行之前都有标签,顺便说一句),我的查找和替换词是:

color\n\n\t\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\n\t\# Color

color\n\n\tGtkWindow::resize-grip-height = 0\n\tGtkWindow::resize-grip-width = 0\n\n\t\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\n\t\# Color

我想出了这个又大又丑的 perl 命令:

perl -pi -e 's/color\n\n\t\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\n\t\# Color/color\n\n\tGtkWindow::resize-grip-height = 0\n\tGtkWindow::resize-grip-width = 0\n\n\t\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\n\t\# Color/g' /usr/share/themes/Ambiance/gtk-2.0/gtkrc

编辑:来自 Zaid 的更正代码:

perl -0777 -pi -e 's/color\n\n\t\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\n\t\# Color/color\n\n\tGtkWindow::resize-grip-height = 0\n\tGtkWindow::resize-grip-width = 0\n\n\t\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\#\n\t\# Color/g' /usr/share/themes/Ambiance/gtk-2.0/gtkrc

如前所述,似乎是 \n\n 会导致问题,因为其他任何东西都可以替换。有什么更好的方法来做到这一点?

我在 Ubuntu 11.10 上,命令将从 shell 脚本而不是 perl 脚本运行。

【问题讨论】:

    标签: perl replace


    【解决方案1】:

    您需要一次性加载整个文件才能检测到\n\n。使用-0777 覆盖默认的逐行行为:

    $ perl -0777 -pi -e 's/\n\n/TEST/g' myfile.xml
    

    【讨论】:

    • 完美。感谢您快速准确的回答。我已经用正确的用法更新了我的问题。
    • +1 虽然你说“你需要”时夸大了。您只需要使用任何其他输入记录分隔符而不是单个换行符来读取文件。
    • 您的问题经常被问到,“我在匹配多行时遇到问题。怎么了?” learn.perl.org/faq/…
    • @tadmc- 对不起,我确实尝试过搜索,但我的搜索是针对我的问题的。
    • @Zaid,他并不需要加载整个文件,他可以将其用作记录分隔符,因此"\n\n" 将位于末尾。
    【解决方案2】:

    当您打开文件进行读取时,默认情况下会逐行读取该文件。这是因为输入记录分隔符$/ 设置为换行符。

    由于您在换行符上打破每个“行”,因此您永远无法在一行中找到两个换行符。

    解决此问题的一种方法是,正如 Zaid 所说,使用 -0 标志更改输入记录分隔符。只要您的正则表达式没有部分匹配新的输入记录分隔符,您就可以了。 (只要您不尝试将. 匹配到\n)。

    让我们让你的正则表达式不那么可怕。你不需要转义#,除非你使用/x修饰符。您不需要连续使用多个# 字符,使用量词+, * or {x,y}

    除了删除一个字符串然后放回一个相同的字符串之外,还有一些选项可以避免输入两次相同的内容。

    • 您可以避免使用Lookaround Assertions 删除字符串。
    • 您可以使用\K 作为简化的后视断言(见上文)
    • 您可以使用$1, $2 ... 捕获字符串并将它们放回原处。

    在这种情况下,我的偏好是使用后向断言来查找“color\n\n”字符串,然后使用前瞻来查找“Color”注释。

    perl -0777 -pwe 's/(?<=color\n\n)(?=[#\s]+Color)/INSERT\n\n/' /path/to/file 
    

    INSERT 当然是您要插入的文本,为了便于阅读,我将其删除。我还删除了-i 标志,因此您可以先尝试一下。

    【讨论】:

    • 我会试一试,谢谢。我假设我需要转义所有散列,以便在从 shell 脚本运行时不会将其解释为注释。正如您所指出的,我的解决方案是可怕的。您对捕获字符串的使用更加优雅。我才刚接触 perl 2 天,还有很多东西要学。
    • @user30441 这不是捕获,而是环视断言。在 shell 脚本中使用时,元字符会变得很麻烦。将代码放入脚本并调用它可能会容易得多。例如。 perl /home/script.pl.
    • 感谢您的澄清。我试图避免调用任何外部脚本,您的方法运行良好。
    猜你喜欢
    • 2011-02-22
    • 2023-01-30
    • 1970-01-01
    • 2017-05-27
    • 2021-10-04
    • 1970-01-01
    • 2010-10-23
    • 2013-05-20
    • 2018-10-23
    相关资源
    最近更新 更多