【问题标题】:Substitute first character before match替换匹配前的第一个字符
【发布时间】:2013-07-04 22:07:29
【问题描述】:

对于每一行,我需要在字母数字符号的第一个匹配之前添加一个分号,但仅适用于第一次出现分号之后的字母数字符号。

例子:

输入:

00000001;Root;;
00000002;  Documents;;
00000003;    oracle-advanced_plsql.zip;file;
00000004;  Public;;
00000005;  backup;;
00000006;    20110323-JM-F.7z.001;file;
00000007;    20110426-JM-F.7z.001;file;
00000008;    20110603-JM-F.7z.001;file;
00000009;    20110701-JM-F-via-summer_school;;
00000010;      20110701-JM-F-via-summer_school.7z.001;file;

期望的输出:

00000001;;Root;;
00000002;  ;Documents;;
00000003;    ;oracle-advanced_plsql.zip;file;
00000004;  ;Public;;
00000005;  ;backup;;
00000006;    ;20110323-JM-F.7z.001;file;
00000007;    ;20110426-JM-F.7z.001;file;
00000008;    ;20110603-JM-F.7z.001;file;
00000009;    ;20110701-JM-F-via-summer_school;;
00000010;      ;20110701-JM-F-via-summer_school.7z.001;file;

有人可以帮我创建 Perl 正则表达式吗?我需要在程序中使用它,而不是作为单线器。

【问题讨论】:

  • 你试过什么?请向我们展示您自己的尝试。这使我们更容易告诉您哪里出了问题以及如何解决它。另外,你是把整个输入放在一个字符串中,还是逐行读取?
  • 其实我不知道如何创建一个符合我要求的正则表达式。我知道基本的正则表达式,并试图获得更多关于它的知识。然而,教程非常冗长(perldoc.perl.org/perlre.html)。我唯一的想法是,像这样索引每一行 while ( $result != -1 ) { $offset = $result + 1; $result = index($string, $char, $offset);并检查空格何时结束。我使用数组逐行读取输入。但这不再是正则表达式了。
  • 试试this tutorial

标签: regex perl csv substitution


【解决方案1】:

这是一种在第一个分号和空格之后,但在第一个非空格之前插入分号的方法。

s/;\s*\K(?=\S)/;/

如果您觉得有必要,您可以使用\w 而不是\S,但我觉得这个输入是不必要的规范。

\K(保持)转义类似于后向断言,因为它不会删除匹配的内容。前瞻断言也是如此,所以这个替换所做的只是在指定位置插入一个分号。

【讨论】:

  • 请注意,这仅在逐行读取输入时有效。否则,您将在模式的开头添加 ^[^;]*,并添加 mg 修饰符。
  • @m.buettner 不,您不能只添加^,您需要插入^[^;]* 或类似的东西。但既然我们可以选择不让事情复杂化,那么最好还是选择不这样做。
  • 哇,当我写我的评论时,你有没有偷看我的肩膀? :P
  • s/;\s*\K(?=\S)/;/ 可以简化为 s/;\s*\K/;/ 如果没有完全由 00000009; 组成的行。
【解决方案2】:

首先,这是一个似乎符合您要求的程序:

#/usr/bin/perl -w
while(<>) {                                                           
  s/^(.*?;.*?)(\w)/$1;$2/;                                            
  print $_;                                                           
}                                                                     

将其存储在文件“program.pl”中,使用“chmod u+x program.pl”使其可执行,然后在您的输入数据上运行它,如下所示:

program.pl input-data.txt

下面是正则表达式的解释:

s/        # start search-and-replace regexp
  ^       # start at the beginning of this line
  (       # save the matched characters until ')' in $1
    .*?;  # go forward until finding the first semicolon
    .*?   # go forward until finding... (to be continued below)
  )
  (       # save the matched characters until ')' in $2
    \w    # ... the next alphanumeric character.
  )
/         # continue with the replace part
  $1;$2   # write all characters found above, but insert a ; before $2
/         # finish the search-and-replace regexp.

根据您的示例输入,我将使用更具体的正则表达式:

s/^(\d*; *)(\w)/$1;$2/;

此表达式从行首开始,跳过数字 (\d*),后跟第一个分号和空格。在后面的单词字符前插入一个分号。

选择最适合您需求的!

【讨论】:

  • 非常感谢您的回答!
【解决方案3】:

首先感谢您的出色回答!

实际上我的代码 sn-p 是这样的:

 our $seperator=";" # at the beginning of the file
 #...
 sub insert {
    my ( $seperator, $line, @all_lines, $count, @all_out );
    $count     = 0;
    @all_lines = read_file($filename);

    foreach $line (@all_lines) {
        $count = sprintf( "%08d", $count );
        chomp $line;
        $line =~ s/\:/$seperator/;                          # works
        $line =~ s/\ file/file/;                            # works

        #$line=~s/;\s*\K(?=\S)/;/;                          # doesn't work
        $line =~ s/^(.*?$seperator.*?)(\w)/$1$seperator$2/; # doesn't work
        say $count . $seperator . $line . $seperator; 

        $count++; # btw, is there maybe a hidden index variable in a foreach-loop I could us instead of a new variable??
        push( @all_out, $count . $seperator . $line . $seperator . "\n" );
    }

    write_file( $csvfile, @all_out ); # using File::Slurp
}

为了得到我提供给你的输入,我已经做了一些小的替换,你可以在 foreach 循环的开头看到。

我很好奇,为什么 TLP 和 Yaakov 提出的正则表达式在我的代码中不起作用。一般来说它们可以工作,但只有在像 Yaakov 给出的示例中那样编写时:

while(<>) {                                                           
  s/^(.*?;.*?)(\w)/$1;$2/;                                            
  print $_;                                                           
}      

【讨论】:

  • 在我看来,当你在 insert() 函数的开头说 my($seperator,...) 时,你隐藏了声明 our $seperator=";"从文件的顶部。从列表 my($seperator...) 中删除变量 $seperator 并再次尝试该程序。
  • 一些常规调试提示: 使用 -w 开关启动您的程序(例如,通过在程序的第一行写入 #!/usr/bin/perl -w)。这将打印有用的警告消息,例如使用未定义的值。另外,我强烈建议把'use strict;'在文件的顶部。这会在编译阶段带来很多错误。
  • 我自己犯了一个愚蠢的错误,我发现了错误,谢谢 Yaakov!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-08-28
  • 2014-08-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-11
  • 1970-01-01
相关资源
最近更新 更多