使用正则表达式为每一行添加节标题

时间:2013-01-07 16:05:39

标签: javascript regex perl vim

我有一个以下格式的文本文件

[Section1]
property1 = value1
property2 = value2

[Section2]
property1 = value1
property2 = value2

一个例子

[Section foo]
foo = 1
bar = "whatever"

有没有办法可以使用像这样的正则表达式为每行添加节标题

Section1: property1 = value1
Section1: property2 = value2

Section2: property1 = value1
Section2: property2 = value2

更新

我没有包含编程语言或工具,所以这里列出了可能性

  1. JavaScript
  2. 的Perl
  3. VIM
  4. 对包括非正则表达式在内的任何其他建议开放。

6 个答案:

答案 0 :(得分:3)

是的,你可以这样做。首先,您必须捕获以下正则表达式

\[(Section\d+)\]\n(.+?\n)|(?:.*?\n)\[Section\d+\]

应捕获您的部分标签以及要将其应用到的所有行。之后,应该是简单的搅拌连接

<强> Regexplanation

()     : a capturing group  
(?:..) : non-capturing group  
\d+    : 1 or more digits  
.+?\n  : 1 or more characters and newline(the '?' means it's non-greedy)  
.*?\n  : 0 or more characters and newline

答案 1 :(得分:2)

这是一个Vim解决方案 - 只需打开文件并运行此命令:

:g/^\[.*\]$/ s/^\[// | s/\]$/:/ | d | ,/\n$/ normal PJ

这将选择截面标题行,将每个标题行转换为所需的形式,以便为该部分中的其他行添加前缀,删除标题行,并将其插入到该部分中所有其他行的开头。

详细说明:

  • :g//选择文件中与模式匹配的行,并将以下命令应用于每个行。在这种情况下,模式匹配从[开始并结束]
  • 的行
  • 第一个:s///删除了开头[,第二个]将结束:更改为|。没有必要添加尾随空格,因为连接行(见下文)将会这样做。
  • :分开多个:g//命令,允许在初始:d范围内执行多项操作。
  • ,删除该行。这也将它存储在可以粘贴的寄存器中。这意味着'当前'行现在是该部分中的第一个属性行。
  • 前缀需要添加到该部分中的所有行。 ,定义范围。
  • 范围的开头是当前行。这是默认值,因此在\n
  • 之前不需要任何内容
  • 范围中的最后一行是空白行(或文件末尾)之前的行。这需要在考虑范围结束后检查线。 $执行此操作,匹配行尾的换行符并将模式超过它,转到下一行(如果有的话)。如果该点是一行的末尾,与\n匹配,那么必须有一个空白行(因为在,/\n$/之后有另一个行尾)或者我们在文件的结尾。
  • 因此:put!定义了需要加前缀的行范围。
  • :join会将最近删除的行插入当前的行之前,使刚插入的行成为当前行,然后:put将该行与下一行连接,插入一个空格它们之间。我们希望为该范围内的每一行做到这一点。
  • 但是:g//没有范围,只是一条线。通常,要将命令应用于范围中的每一行,可以使用:g//。但是在这里我们已经在:normal命令中了,它们不能嵌套。
  • 幸运的是,P将指定的正常模式击键应用于范围中的每一行。在正常正常模式下按:put!J命令相同,:join,/\n$/ normal PJ相同。
  • 所以:normal表示对于从当前到空白行(或文件末尾)之前的每行的每一行,将最近删除的前缀粘贴到它上面,然后将现有行加入到结尾前缀。
  • |不能跟随另一个命令,因为任何:normal都将被解释为普通模式击键而不是命令分隔符。因此,通常在命令序列中使用:exe需要将其包装在:normal中。但是在这种情况下:g//是我们想要做的最后一件事,所以它可以保留在命令末尾。
  • 上述内容改变了第一部分后,Vim会移动到与{{1}}匹配的第二部分标题,并再次进行。

答案 2 :(得分:1)

这可以通过单行完成:

perl -F"\n" -00 -anwE '$h = shift @F; 
                       $h =~ s/^\[|\]$//g; 
                       say "$h: $_" for @F; 
                       say $/;' paragraph.txt

使用此代码,我们使用段落模式从文件中读取行块,在新行上自动分割每个块,然后取消块的第一行,清理它并将其用作生成的打印中的标题({ {1}})。

  • say将输入记录分隔符设置为空字符串以启用段落模式,即读取直到下一个双换行符。
  • -00自动将每行输入分为-a
  • @F开关允许我们将换行符设置为autosplit
  • 的分隔符
  • -F-E类似,但启用了-e
  • 等功能

代码在脚本形式中如下所示:

say

答案 3 :(得分:0)

此正则表达式捕获组中的值:

\[(?<Section>.*?)\]\r\n(?<p1>property\d)\s=\s(?<v1>value\d)\r\n(?<p2>property\d)\s=\s(?<v2>value\d)

这个用于替换前面的捕获:

${Section} : ${p1} = ${v1} 
${Section} : ${p2} = ${v2}

Expresso工具的结果是:

  

第1节:property1 = value1
  第1节:property2 = value2

     

第2节:property1 = value1
  第2节:property2 = value2

必须对其进行优化以使其成为通用

答案 4 :(得分:0)

尝试一下这个Perl单线的内容:

perl -n -wE 'if (/^\[(\w+)\]$/) { $section = $1; } else { s/^(?=\S)/$section: /; print; }' yourfile.txt

-nperl一次处理文件的每一行。如果该行看起来像一个部分标题,那么它将存储该部分名称以供将来使用。否则,它会将先前存储的部分名称添加到以可打印字符开头的任何行,然后打印该行。

答案 5 :(得分:0)

这是使用perl <scriptName> <dataFile>从命令行运行的另一个选项:

use warnings;
use strict;

my $section;
while (<>) {
    if (/^\[(Section[^\]]*)\]$/) {
        $section = $1;
        next;
    }
    print /\S/ ? "$section: $_" : $_;
}