【问题标题】:PERL - work with txt files, and extracting the data in different variablesPERL - 使用 txt 文件,并以不同的变量提取数据
【发布时间】:2021-09-26 23:28:53
【问题描述】:

我需要处理 .txt 文件,并按文件名中存储的名称和日期进行过滤。

目前我实现了以下目标:

my $dir = "t-files\/";
chdir($dir);
foreach $files (glob('*.txt')) {
  ($sname) = split(/_/, $files);
  #($sdate) = "still under work"
  print "\nSwitch Name: $sname - Date: still under work";
}

文件示例名称:"s-ar-ar55g-1_20140911-09.txt" | "s-ar-ar55g-1_20141027-09.txt" |

使用此脚本,我有以下输出:

D:\_perl>test_01.pl

Switch Name: s-ar-ar55g-1 - Date: still under work
Switch Name: s-ar-ar55g-1 - Date: still under work
Switch Name: s-ar-ar55g-1 - Date: still under work
Switch Name: s-ar-ar55g-1 - Date: still under work
Switch Name: s-ar-ar55g-1 - Date: still under work
Switch Name: s-ar-ar55g-1 - Date: still under work
Switch Name: s-ar-ar55g-1 - Date: still under work
Switch Name: s-ar-ar55g-1 - Date: still under work
Switch Name: s-ar-ar55g-1 - Date: still under work
D:\_perl>

我的意图是从文件中提取日期字符串“20140911”,并存储到一个新变量“sdate”中

通过这种方式我需要有两个变量,所以我可以与名称和日期进行比较

是否可以直接从txt文件名中提取像“20140911”这样的年月日?

【问题讨论】:

    标签: regex perl split


    【解决方案1】:

    总是可以用简单的正则表达式解析这样的字符串

    my $file = 's-ar-ar55g-1_20140911-09.txt';
    
    my ($sname, $date) = $file =~ /( [^_]+ ) _ ( [0-9]{8} )/x;
    

    /x 修饰符使它忽略模式中的空格(和换行符,并尊重带有# 的 cmets),以便我们可以使其更具可读性。至于模式,我在字符类[][^_]中使用否定(^),匹配_以外的任何字符,而后面的+表示必须至少有一个这样的字符.这样就匹配一个字符串,直到第一个 _

    这是因为包围了() 而被捕获的,因此必须重复8 次的数字[0-9]{8} 的模式也是如此。返回两个捕获的模式,并分配给$sname$date。对于初学者,请参阅教程 perlretut,或您最喜欢的优秀 Perl 书籍。

    请注意,我声明了my $sname,以及所有其他引入的变量。这可以通过strict pragma 强制执行,当然您也必须始终启用warnings


    您使用的split 是一个很好的工具,但这里还有一些事情要做

    my ($sname, $date) = split /_/, $file;  
    # Now need to remove the trailing `-1.txt` from $date
    ($date) = split /-/, $date, 2;
    # or, with a regex
    # $date =~ s/[^-]+\K.*//;  # remove the first - and all after it
    

    第二个split 中的第三个参数2 告诉split 一起返回两个元素。所以这将是第一个 - 之前的内容,然后是一个包含所有内容的字符串。

    我们需要在$date 周围使用() 来强制执行list context,否则它将强加一个标量上下文并分配返回列表的元素数(2)。

    显然比基本的正则表达式 usd 首先需要更多的工作和考虑。

    为了进一步推动这一论点,另一种方法是在_- 上联系split,然后根据需要组装零件

    my @parts = split /[_-]/, $file;
    my ($sname, $date) = ( join('-', @parts[0..3]), $parts[4] );
    

    现在我们还有 @parts 变量浮动,据说是不需要的,所以让我们避免命名空间污染

    my ($sname, $date) = do {
        my @parts = split /[_-]/, $file;
        join('-', @parts[0..3]), $parts[4];
    };
    

    (现在@parts,在do 块内被声明为词法my,在它之外不存在。)

    当字符串的一部分需要分析和处理时,这是一种处理字符串的标准方法,但与那个简单的正则表达式相比,这显然是一种矫枉过正。

    【讨论】:

    • 你好 zdim!非常感谢,这对我正在做的工作非常方便
    • @seltika 很高兴听到它有帮助 :) 如果更多解释和/或参考资料有用,请告诉我
    【解决方案2】:

    以下代码 sn-p 利用正则表达式从文件名中提取/捕获 4 部分:下划线之前的任何内容、年份(前 4 位数字)、月份(接下来的 2 位数字)、月份中的日期(接下来的 2 位数字)——为了理智check 需要带有以下 2 位数字的破折号,点和 txt 作为文件的扩展名。

    输出将日期部分与/ 连接起来仅用于演示目的。

    注意: 将 while( <DATA> ) { 替换为 for ( glob('s-ar-*.txt') ) { 以获取与文件系统中文件 mask 匹配的文本文件列表。

    use strict;
    use warnings;
    use feature 'say';
    
    while( <DATA> ) {
        /([^_]*)_(\d{4})(\d{2})(\d{2})-\d{2}\.txt/;
        my($switch,$year,$month,$mday) = ($1,$2,$3,$4);
        say "Switch name: $switch - Date: " . join('/',$year,$month,$mday);
    }
    
    
    __DATA__
    s-ar-ar55g-1_20140911-09.txt
    s-ar-ar55g-1_20141027-09.txt
    

    输出

    Switch name: s-ar-ar55g-1 - Date: 2014/09/11
    Switch name: s-ar-ar55g-1 - Date: 2014/10/27
    

    参考:Perl regular expression

    【讨论】:

    • 北极熊你好!非常感谢,你的回答也很方便!!
    猜你喜欢
    • 2012-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多