【问题标题】:Perl: How to add a line to sorted text filePerl:如何在已排序的文本文件中添加一行
【发布时间】:2012-08-26 04:53:53
【问题描述】:

我想在 perl 中的文本文件中添加一行,其中包含排序形式的数据。我已经看到了一些示例,这些示例展示了如何在文件末尾附加数据,但是因为我希望数据采用排序格式。

请指导我如何做到这一点。

基本上从我到目前为止所尝试的: (我打开一个文件,对其内容进行 grep 以查看我要添加到文件中的行是否已经存在。如果存在,则退出,否则将其添加到文件中(这样数据保持排序格式)

open(my $FH, $file) or die "Failed to open file $file \n";
@file_data = <$FH>;
close($FH);
my $line = grep (/$string1/, @file_data);
if($line) {
   print "Found\n";
   exit(1);
}
else
{
  #add the line to the file
  print "Not found!\n";
}

【问题讨论】:

标签: perl file


【解决方案1】:

这是一种使用Tie::File 的方法,这样您就可以轻松地将文件视为一个数组,并使用List::BinarySearchbsearch_str_pos 函数快速找到插入点。找到插入点后,检查该点的元素是否等于插入字符串。如果不是,splice 将其放入数组中。如果相等,则不要拼接。并以untie 结束,以便文件干净地关闭。

use strict;
use warnings;
use Tie::File;
use List::BinarySearch qw(bsearch_str_pos);

my $insert_string = 'Whatever!';
my $file          = 'something.txt';

my @array;
tie @array, 'Tie::File', $file or die $!;

my $idx = bsearch_str_pos $insert_string, @array;

splice @array, $idx, 0, $insert_string
    if $array[$idx] ne $insert_string;

untie @array;

来自List::BinarySearchbsearch_str_pos 函数是对来自Mastering Algorithms with Perl 的二分搜索实现的改编。它方便的特点是如果没有找到搜索字符串,它会返回可以插入的索引点,同时保持排序顺序。

【讨论】:

  • 先生哦!感谢您指出要用于巨大文件的优秀库。但是有一个问题:如果 - 看起来 - 几乎不可能实现 b 搜索,我们如何确定您的搜索是正确的?
  • 二分搜索显然不是不可能正确实现的——它非常基础。但是会发生错误,理论上,如果很多人都看过并使用了代码,就会比少数人使用的一次性解决方案更快地发现错误。源代码是公开可用的,欢迎提交合理的错误报告和请求请求。
  • 对于那些想要真正做巨大(11gigs,300m 记录)文件的人 - 像我一样(如果你想知道我在做什么,请点击这里haveibeenpwned.com/Passwords)File:tie 实际上完全落在它的face :( 100% cpu,RAM 使用率飙升,疯狂交换。在它杀死我的工作站之前杀死它。
  • 基于磁盘的二进制搜索是可能的,但您必须解决查找记录从哪里开始的问题,而无需在每次需要搜索时读取整个文件。构建索引或索引文件可以是一种方式。或者使用固定的记录大小。或者构建排序键的索引......解决问题的几种方法,但每种方法都有自己的挑战。
  • 对。记录确实是固定大小(40 字节 SHA1-hex-hash + 换行符),因此我在 python 中的第一个实现是将文件按字节大小减半,然后将一些字符回滚到下一个完整行并抢夺一个值。冲洗并重复。因为我必须将它与本机 Perl 代码集成,所以我想我会尝试 libs,因为我对我拼凑起来的概念证明缺乏信心^^ 每次尝试需要 20 到 50 毫秒。如果你想要代码,请私信我(对于 github 来说太丑了)。
【解决方案2】:

既然您无论如何都必须阅读文本文件的内容,那么换一种方法怎么样?

逐一读取文件中的行,并与您的目标字符串进行比较。如果您读取的行等于目标字符串,则无需执行任何操作。

否则,您最终会根据排序标准读取比当前行“更大”的行,或者您到达文件末尾。在前一种情况下,您只需在该位置插入字符串,然后复制其余行。在后一种情况下,您将字符串附加到末尾。

如果您不想那样做,您可以在@file_data 中进行二进制搜索以找到添加行的位置,而无需检查所有条目,然后在输出之前将其插入到数组中数组到文件中。

【讨论】:

    【解决方案3】:

    这是一个简单的版本,它从标准输入(或在命令行中指定的文件名)读取,如果在输入中找不到,则将“要附加的字符串”附加到输出。输出打印在标准输出上。

    #! /usr/bin/perl 
    
    $found = 0;
    $append='string to append';
    
    while(<>) {
        $found = 1 if (m/$append/o);
        print
    }
    
    print "$append\n" unless ($found);;
    

    修改它以就地编辑文件(使用 perl -i)并从命令行获取附加字符串将非常简单。

    【讨论】:

      【解决方案4】:

      在不使用任何模块的情况下插入一行的“简单”单行可能是:

      perl -ni -le '$insert="lemon"; $eq=($insert cmp $_); if ($eq == 0){$found++}elsif($eq==-1 && !$found){print$insert} print'
      

      给出一个list.txt,其上下文是:

      ananas
      apple
      banana
      pear
      

      输出是:

      ananas
      apple
      banana
      lemon
      pear
      

      【讨论】:

      • 这是最好的解决方案,但它确实可以提高可读性。它实际上不必是单行的。您可以使用$^I 全局变量激活就地编辑模式。
      【解决方案5】:
      {
        local ($^I, @ARGV) = ("", $file); # Enable in-place editing of $file
      
        while (<>) {
          # If we found the line exactly, bail out without printing it twice
          last if $_ eq $insert;
          # If we found the place where the line should be, insert it
          if ($_ gt $insert) {
            print $insert;
            print;
            last;
          }
          print;
        }
        # We've passed the insertion point, now output the rest of the file
        print while <>;
      }
      

      与帕维尔的答案基本相同,除了增加了很多可读性。请注意,$insert 应该已经包含一个尾随换行符。

      【讨论】:

        猜你喜欢
        • 2011-04-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-04-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多