1

我有一个包含文本的 CSV 文件,其中包含新的换行符。例如

1,b,hello
world,x
2,a,hello

mars,y

要一次连续阅读所有内容,我想为 $/ 特殊变量指定一个正则表达式。关于我如何做到这一点的任何建议?

我的想法是,如果我为我的特殊变量输入类似 "(x|y)\n" 的内容,它应该捕获行以 x 或 y 以及新行结尾的情况。

谢谢

4

3 回答 3

4

您不能对 $/ 使用正则表达式。但是,如果文件不是太大,您可以将整个内容读入一个标量并在正则表达式上拆分。

@records = split /(x|y)\n/, $data;

于 2012-08-23T18:37:14.940 回答
3

没有将这样的文件分成记录的通用方法,因为无法判断文件中的一行是更多当前记录还是新记录的开始。

但是,如果您可以假设

  • 一条记录中总是有相同数量的字段

  • 字段中的数据从不包含逗号

  • 记录的最后一个字段永远不会跨行拆分

然后你可以简单地从文件中累积行,直到你有足够数量的字段

这个程序演示了这个原理。

use strict;
use warnings;

while (my $record= <>) {
  $record .= <> until $record =~ tr/,// == 3;
  print ">> $record\n";
}

输出

>> 1,b,hello
world,x

>> 2,a,hello

mars,y
于 2012-08-23T19:15:54.220 回答
2

您可以创建自己的子程序以一次读取一个数据集:

 sub readDataSet {
   my $buffer = '';
   local $/ = "\n";
   $buffer .= <STDIN> until $buffer =~ /(x|y)\n$/;
   return $buffer;
 }

 my $nextRow = readDataSet();

这将返回一整行。我可以这样做,因为您的 Regexp 以恒定部分结尾。这个子有几个变体:

  1. 从任何文件句柄读取:

    sub readDataSet {
      my ($filehandle) = @_;
      my $buffer = "";
      $buffer .= <$filehandle> until $buffer =~ /(x|y)\n$/;
      return $buffer;
    }
    
    open my $fh, "<", $filename or die;
    my $nextRow = readDataSet($fh);
    
  2. 构造一个匿名子来读取。Filehandle 在构造函数 sub 中只提供一次。这有点面向对象。

    sub newDataSetReader {
      my ($filehandle) = @_;
      return sub {
        my $buffer = '';
        local $/ = "\n";
        $buffer .= <$filehandle> until $buffer =~ /(x|y)\n$/;
        return $buffer;
      };
    }
    
    open my $fh, "<", $filename or die;
    my $reader = newDataSetReader($fh);
    my $nextRow = $reader->();
    

    我更喜欢最后一种解决方案,但只有从多个文件中读取才有意义。

当您通过 sub 阅读时,您可以轻松插入调试钩子或预过滤数据,例如将行拆分为字段并返回一个数组而不是单个字符串。

于 2012-08-23T19:11:53.247 回答