2

我有以下格式的文本文件

[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. 虚拟机

接受任何其他建议,包括非正则表达式。

4

6 回答 6

3

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

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

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

正则解释

()     : 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
于 2013-01-07T16:13:10.447 回答
2

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

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

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

详细地:

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

这可以通过单线来完成:

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

使用这段代码,我们使用段落模式从文件中读取行块,在换行符上自动拆分每个块,然后取出块的第一行,清理它并将其用作生成的 print ( say) 中的标题。

  • -00将输入记录分隔符设置为空字符串以启用段落模式,即阅读直到下一个双换行符。
  • -a将每行输入自动拆分为@F
  • -Fswitch 允许我们将换行符设置为自动拆分的分隔符
  • -E喜欢-e但启用功能,例如say

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

use warnings;
use strict;

$/ = "";                      # paragraph mode, read until "\n\n"
use feature 'say';            # enable 'say'
while (<>) {                 
    my @F = split /\n/;       
    my $h = shift @F;         
    $h =~ s/^\[|\]$//g;       # clean up lines
    say "$h: $_" for @F;      
    say $/;                   # print paragraph ending 
}
于 2013-01-07T16:59:22.947 回答
0

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

\[(?<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 节:属性 1 = 值 1第 1
节:属性 2 = 值 2

第 2节:属性 1 = 值 1
第 2 节:属性 2 = 值 2

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

于 2013-01-07T16:47:30.887 回答
0

试试这个 Perl 单行代码:

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

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

于 2013-01-07T16:49:33.090 回答
0

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

use warnings;
use strict;

my $section;
while (<>) {
    if (/^\[(Section[^\]]*)\]$/) {
        $section = $1;
        next;
    }
    print /\S/ ? "$section: $_" : $_;
}
于 2013-01-07T17:54:40.270 回答