Perl:Regex 不抓取代码中的多行 C 风格注释

Perl: Regex not grabbing multiline C style comments in code

我有一个 Perl 程序:

#!/usr/bin/perl
use warnings;
use strict;

my $src = 'D:\Scripts\sample.c';
my $fileName;

# open source file for reading
open(SRC_FH,'<',$src) or die $!;

while(my $row = <SRC_FH>){
    if ($row =~ /([0-9]{2}\.[0-9]{2}\.[0-9]{3}\.[a-z,0-9]{2}|[0-9]{2}\.[0-9]{2}\.[0-9]{3}\.[a-z,0-9]{3})/){
        $fileName = ;
    }
}

my $des = "D:\Scripts\" . $fileName . ".txt";

# open destination file for writing
open(DES_FH,'>',$des) or die $!;

print("copying content from $src to $des\n");

seek SRC_FH, 0, 0;

while(my $row = <SRC_FH>){
    if ($row =~ /(\/\*.*abcd.[\s\S]*?\*\/)/){
        print DES_FH "\n";
    }
}

# always close the filehandles
close(SRC_FH);

close(DES_FH);
print "File content copied successfully!\n";

我的问题是,尽管正则表达式是正确的,但我认为由于 perl 代码的执行方式,我的目标文件只获得了写入的 1 行注释。任何超过 1 行的 C 风格注释都不会写入目标文件。我在第二个 if 语句中遗漏了什么?

我在此处检查了我的第二个 if 语句正则表达式 https://regexr.com/,它可以捕获多行 C 样式注释以及还包含关键字 abcd 的单行注释。

所以我尝试了 zdim 下面的第一个建议。这是我使用的:

#!/usr/bin/perl
use warnings;
use strict;

my $src = 'D:\Scripts\sample.c';
my $fileName;
my @comments;

# open source file for reading
open(SRC_FH,'<',$src) or die $!;

while(my $row = <SRC_FH>){
    if ($row =~ /([0-9]{2}\.[0-9]{2}\.[0-9]{3}\.[a-z,0-9]{2}|[0-9]{2}\.[0-9]{2}\.[0-9]{3}\.[a-z,0-9]{3})/){
        $fileName = ;
    }
}

my $des = "D:\Scripts\" . $fileName . ".txt";

# open destination file for writing
open(DES_FH,'>',$des) or die $!;

print("copying content from $src to $des\n");

#seek SRC_FH, 0, 0;

my $content = do {
    #read whole file at once
    local $/;
    open (SRC_FH,'<', $src) or die $!;
    <SRC_FH>;
};

#if($content =~ /(\/\*.*abcd.[\s\S]*?\*\/)/sg){
#       my @comments = $content;
#   }

my @comments = $content =~ /(\/\*.*abcd.[\s\S]*?\*\/)/sg;

foreach (@comments){
    print DES_FH "\n";
}

#while(my $row = <SRC_FH>){
#   if ($row =~ /(\/\*.*abcd.[\s\S]*?\*\/)/){
#       print DES_FH "\n";
#   }
#}

# always close the filehandles
close(SRC_FH);

close(DES_FH);
print "File content copied successfully!\n";

结果是 sample.c 中的所有内容都被复制到目标文件中。完整的 1:1 副本。我希望从 C 文件中提取所有单行和多行注释。

示例 1: /* A B C D */ 示例 2: /* 一些文本 * 更多评论 abcd 和更多评论 */

最终解决方案

#!/usr/bin/perl
use warnings;
use strict;

my $src = 'D:\Scripts\sample.c';
my $fileName;

# open source file for reading
open(SRC_FH,'<',$src) or die $!;

while(my $row = <SRC_FH>){
    if ($row =~ /([0-9]{2}\.[0-9]{2}\.[0-9]{3}\.[a-z,0-9]{2}|[0-9]{2}\.[0-9]{2}\.[0-9]{3}\.[a-z,0-9]{3})/){
        $fileName = ;
    }
}

my $des = "D:\Scripts\" . $fileName . ".txt";

# open destination file for writing
open(DES_FH,'>',$des) or die $!;

print("copying content from $src to $des\n");

seek SRC_FH, 0, 0;

my $content = do{local $/; <SRC_FH>};

my @comments = $content =~ /(\/\*.*abcd.[\s\S]*?\*\/)/g;

for(@comments){
    print DES_FH "$_\n";
}

# always close the filehandles
close(SRC_FH);

close(DES_FH);
print "File content copied successfully!\n";

What am I missing in my 2nd if statement?

嗯,没什么——只是在多行 C 注释中,它的两行都没有 /**/。因此,当逐行读取文件时,正则表达式无法匹配多行注释。

要捕捉此类评论:

  • 将整个文件读入一个字符串(“吞噬”它),然后在正则表达式上添加 /s 修饰符,以便 . 也匹配换行符。还可以使用 /g 修饰符 so 来捕获字符串中的所有此类模式。一种方式

    my $content = do { 
        local $/;  # undef record separator so the whole file is read at once
        open my $src_fh, '<', $src_file or die $!;  # have to re-open
        <$src_fh>;                                  # reads it all
    };  # lexical filehandle gets closed as we leave scope
    
    # NOTE -- there may be difficulties in capturing comments in a C source file
    my @comments = $content =~ /.../sg;  # your regex
    

    或者使用一个库来 slurp 一个文件,比如

    use Path::Tiny;
    my $content = path($src_file)->slurp; 
    

或者,

  • 当您看到 /*、get/print 所有行时设置一个标志,直到您点击结束 */,然后取消设置标志。这是它的基本版本

    my $inside_comment = 0;
    while (<$src_fh>) {
        if (m{(/\*.*)}) {         #/ fix syntax hilite
            $inside_comment = 1;  # opening line for the comment 
            say $des_fh ; 
        } 
        elsif (m{(.*\*/)}) {      # closing line for the comment
            say $des_fh ; 
            $inside_comment = 0; 
        } 
        elsif ($inside_comment) { say $des_fh $_}
    }
    

    我测试了所有这些,但请检查并改进。其一,前导空格很有趣。

注意:一般来说,从 C 程序中获取所有注释可能相当棘手。


这是一个 one-line 版本的 slurping

my $file_content = do { local (@ARGV, $/) = $file_name; <> }