【问题标题】:What is the best way to slurp a file into a string in Perl?在 Perl 中将文件转换为字符串的最佳方法是什么?
【发布时间】:2010-09-17 10:12:59
【问题描述】:

是的,There's More Than One Way To Do It,但必须有一个规范或最有效或最简洁的方法。我会添加我知道的答案,看看有什么内容会渗透到顶部。

要清楚,问题是如何最好地将文件的内容读入字符串。 每个答案一个解决方案。

【问题讨论】:

    标签: perl string file-io file


    【解决方案1】:

    这个怎么样:

    use File::Slurp;
    my $text = read_file($filename);
    

    预计到达时间:注意 Bug #83126 for File-Slurp: Security hole with encoding(UTF-8)。我现在推荐使用File::Slurper(免责声明:我写的),也是因为它在编码方面有更好的默认值:

    use File::Slurper 'read_text';
    my $text = read_text($filename);
    

    Path::Tiny:

    use Path::Tiny;
    path($filename)->slurp_utf8;
    

    【讨论】:

    • 如果文件不存在,如果你不想让它死掉怎么办?
    • 防止这种情况发生的最简单方法是首先检查文件是否存在...
    • 这确实有一个缺点,即它不包含在开箱即用的 perl 中。至少不是我的 ActiveState perl for windows (v5.10.0)。
    • 请注意,最近发现 File::Slurp 是一个巨大的安全问题:rt.cpan.org/Ticket/Display.html?id=83126
    • 嗨,我收到了Undefined subroutine &main::read_text。应该是use File::Slurper 'read_text';metacpan.org/pod/File::Slurper
    【解决方案2】:

    我喜欢使用 do 块执行此操作,我在其中本地化 @ARGV,因此我可以使用菱形运算符为我执行文件魔术。

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

    如果你需要它更健壮一点,你可以很容易地把它变成一个子例程。

    如果您需要能够处理各种特殊情况的强大功能,请使用File::Slurp。即使您不打算使用它,也请查看源代码以了解它必须处理的所有古怪情况。 File::Slurp 有一个 big security problem 看起来没有解决方案。部分原因是它未能正确处理编码。即使我的快速回答也有这个问题。如果您需要处理编码(可能是因为默认情况下您没有将所有内容都设为 UTF-8),则此扩展为:

    my $contents = do {
        open my $fh, '<:encoding(UTF-8)', $file or die '...';
        local $/;
        <$fh>;
        };
    

    如果您不需要更改文件,您可以使用File::Map

    【讨论】:

    • 我很懒,写my $contents = do {local (@ARGV,$/) = $file; &lt;&gt;};,用更少的字符写完全一样的东西:)
    • 我想知道为什么本地 @ARGV = $file; 将与 不同。
    • @Bemrose:因为 $file 不是文件句柄。
    • 我把这个方法添加到一个已经在使用&lt;&gt; 的文件中,希望它从STDIN 读取。 &lt;&gt; 的行为不同于第一次调用和后续调用,并且由于我更改了第一次调用,因此我也更改了现有调用的行为(预期 &lt;STDIN&gt; 的行为 &lt;&gt;)。
    【解决方案3】:

    在写File::Slurp(这是最好的方式)时,Uri Guttman 对多种啜饮方式进行了大量研究,哪种方式最有效。他写下了his findings here 并将它们合并到信息 File::Slurp。

    【讨论】:

    【解决方案4】:
    open(my $f, '<', $filename) or die "OPENING $filename: $!\n";
    $string = do { local($/); <$f> };
    close($f);
    

    【讨论】:

      【解决方案5】:

      需要考虑的事情(尤其是与其他解决方案相比时):

      1. 词法文件句柄
      2. 缩小范围
      3. 减少魔法

      所以我得到:

      my $contents = do {
        local $/;
        open my $fh, $filename or die "Can't open $filename: $!";
        <$fh>
      };
      

      我不是魔法的忠实粉丝,除了实际使用魔法。与其伪装出来,不如直接使用 open 调用?这不是更多的工作,而且是明确的。 (真正的魔法,尤其是在处理“-”时,要完美模拟需要做更多的工作,但无论如何我们都不会在这里使用它。)

      【讨论】:

      • 如果对那些在家跟随的人来说不是很明显,在花括号的末尾,$fh 会超出范围并且文件句柄会自动关闭。
      【解决方案6】:

      字符串的mmap(内存映射)可能在以下情况下很有用:

      • 有非常大的字符串,您不想加载到内存中
      • 想要一个盲目的快速初始化(您在访问时获得渐进式 I/O)
      • 对字符串进行随机或惰性访问。
      • 可能想要更新字符串,但只是扩展它或替换字符:
      #!/usr/bin/perl
      use warnings; use strict;
      
      use IO::File;
      use Sys::Mmap;
      
      sub sip {
      
          my $file_name = shift;
          my $fh;
      
          open ($fh, '+<', $file_name)
              or die "Unable to open $file_name: $!";
      
          my $str;
      
          mmap($str, 0, PROT_READ|PROT_WRITE, MAP_SHARED, $fh)
            or die "mmap failed: $!";
      
          return $str;
      }
      
      my $str = sip('/tmp/words');
      
      print substr($str, 100,20);
      

      更新:2012 年 5 月

      在将Sys::Mmap 替换为File::Map 之后,以下内容应该非常等效

      #!/usr/bin/perl
      use warnings; use strict;
      
      use File::Map qw{map_file};
      
      map_file(my $str => '/tmp/words', '+<');
      
      print substr($str, 100, 20);
      

      【讨论】:

      • 实际上,File::Map(免责声明:由我编写)是当今更好的选择。它更便携(适用于 Unix 和 Windows),但也更易于使用(«map_file my $str, $file_name;»)。
      【解决方案7】:
      use Path::Class;
      file('/some/path')->slurp;
      

      【讨论】:

        【解决方案8】:
        {
          open F, $filename or die "Can't read $filename: $!";
          local $/;  # enable slurp mode, locally.
          $file = <F>;
          close F;
        }
        

        【讨论】:

          【解决方案9】:

          这既不快速,也不独立于平台,而且非常邪恶,但它很短(我在 Larry Wall 的代码中看到了这一点 ;-):

           my $contents = `cat $file`;
          

          孩子们,不要在家里这样做;-)。

          【讨论】:

            【解决方案10】:
            use IO::All;
            
            # read into a string (scalar context)
            $contents = io($filename)->slurp;
            
            # read all lines an array (array context)
            @lines = io($filename)->slurp;
            

            【讨论】:

              【解决方案11】:

              查看Perl6::Slurp 的摘要,它非常灵活,通常只需很少的努力就能做正确的事情。

              【讨论】:

                【解决方案12】:

                以下是最流行的方法的一个很好的比较:

                http://poundcomment.wordpress.com/2009/08/02/perl-read-entire-file/

                【讨论】:

                  【解决方案13】:

                  没有人谈论 read 或 sysread,所以这里有一个简单快捷的方法:

                  my $string;
                  {
                      open my $fh, '<', $file or die "Can't open $file: $!";
                      read $fh, $string, -s $file;   # or sysread
                      close $fh;
                  }
                  

                  【讨论】:

                    【解决方案14】:

                    对于单行,你通常可以使用the -0 switch(和-n)让perl一次读取整个文件(如果文件不包含任何空字节):

                    perl -n0e 'print "content is in $_\n"' filename
                    

                    如果是二进制文件,可以使用-0777:

                    perl -n0777e 'print length' filename
                    

                    【讨论】:

                    • 提供了一种检查文件中尝试的行替换是否实际发生的好方法: perl -p -i -0 -e 's/^old_line/new_line/m or (print and die) ' some_file,或者如果很多人期望的话,可能可以使用 /mg 来完成所有匹配的行。
                    【解决方案15】:

                    以最坏的方式做的候选人! (见评论。)

                    open(F, $filename) or die "OPENING $filename: $!\n";
                    @lines = <F>;
                    close(F);
                    $string = join('', @lines);
                    

                    【讨论】:

                    • 这可能是我能想到的最低效的方式,尤其是对于大文件。现在您有两个相同数据的副本,并且您已经处理了两次,只是为了将其加载到一个标量中。
                    • 这都是情境性的。对于小文件或只运行一次的快速脚本,其中 "$string=cat $filename" 不可用,这是完全合理的。低效是的!但这不一定是唯一的考虑因素。
                    • 这个答案不值得负面评价。一群不理解或不关心 的 perl 含义的脚本小子。这是一个愚蠢的数组。没有比此页面上的其他一些答案更差的性能。关于如何将 Perl 文件句柄和 slurping 作为一个数组来思考的非常有用的信息。
                    【解决方案16】:

                    调整特殊记录分隔符变量$/

                    undef $/;
                    open FH, '<', $filename or die "$!\n";
                    my $contents = <FH>;
                    close FH;
                    

                    【讨论】:

                      猜你喜欢
                      • 2011-04-25
                      • 2011-08-11
                      • 1970-01-01
                      • 2014-09-04
                      • 1970-01-01
                      • 2011-08-14
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多