使用perl tie :: file和utf编码文件

时间:2011-10-14 21:00:23

标签: perl

我可以将Tie::File与utf编码的输出文件一起使用吗?我不能让这个工作正常。 我要做的是打开这个utf编码文件,从文件中删除匹配字符串并重命名该文件。

代码:

use strict;
use warnings;
use Tie::File;
use File::Copy;

my ($input_file) = qw (test.txt);

open my $infh, "<:encoding(UTF-16LE)", $input_file or die "cannot open '$input_file': $!";

for (<$infh>) {
    tie my @lines, "Tie::File", $_;
    shift @lines if $lines[0] =~ m/MyHeader/;
    untie @lines;
    my ($name) = /^(.*).csv/i;
    move($_, $name . ".dat");
}

close $infh
    or die "Cannot close '$input_file': $!";

代码:(更新)

my ($input_file) = qw (test.txt);
my $qfn_in = $input_file;
my $qfn_out = $qfn_in . ".dat";

open(my $fh_in, "<:raw:perlio:encoding(UTF-16le):crlf:utf8", $qfn_in)
   or die("Can't open \"$qfn_in\": $!\n");

open(my $fh_out, ">:raw:perlio:encoding(UTF-16le):crlf:utf8", $qfn_out)
   or die("Can't open \"$qfn_out\": $!\n");

while (<$fh_in>) {
   next if $. == 1 && /MyHeader/; 
   print($fh_out $_)
      or die("Can't write to \"$qfn_out\": $!");
}

close($fh_in);
close($fh_out) or die("Can't write to \"$qfn_out\": $!");

rename($qfn_out, $qfn_in)
   or die("Can't rename: $!\n");

3 个答案:

答案 0 :(得分:5)

Tie::File perldoc中未记录此内容,但您希望在绑定文件时传递discipline => ':encoding(UTF-16LE)'选项:

tie my @lines, 'Tie::File', $input_file, discipline => ':encoding(UTF-16LE)'

请注意,第三个参数是与绑定数组关联的文件的名称。 Tie::File会自动为您打开和管理文件句柄;无需亲自调用open文件。

@lines现在包含文件的内容,因此接下来要做的就是检查第一行:

if ($lines[0] =~ m/pattern/) {
    my $line = shift @lines;
    untie @lines;   # rewrites, closes the file, w/o first line
    my ($name) = $line =~ /^(.*).csv/i;
    rename $input_file, "$name.dat";
}

但我同意TLP Tie::File对这项工作来说太过分了。

(我之前的回答是关于使用正确的编码打开文件句柄并将glob作为第三个arg传递给Tie::File将不起作用,因为(1)它没有以读/写模式打开文件(2)即使它确实如此,Tie::File也不能或不会对文件句柄的读取和写入都应用编码。

答案 1 :(得分:4)

该行:

tie my @lines, "Tie::File", $_;

尝试将@lines绑定到具有每行test.txt的名称的文件。由于它似乎不是包含文件名的文件,我怀疑tie失败了。

您在Tie::File上使用test.txt时可能会遇到的问题。如果您只想检查该文件的第一行,则不需要循环。

所以你需要这样的东西:

use autodie;  #handy to check for fatal errors
tie my @lines, "Tie::File", $input_file;
shift @lines if $lines[0] =~ /MyHeader/;
untie @lines;
if ($input_file =~ /(.+).csv/i) {
    move($input_file, $1);
}

但是有更简单的方法来检查文件的第一行。这将检查一个文件:

perl -we '$_=<>; print if /MyHeader/; print <>;' test.txt > test.dat

答案 2 :(得分:4)

my $qfn_in = ...;
my $qfn_out = $qfn_in . ".tmp";

open(my $fh_in, "<:raw:perlio:encoding(UTF-16le):crlf:utf8", $qfn_in)
   or die("Can't open \"$qfn_in\": $!\n");

open(my $fh_out, ">:raw:perlio:encoding(UTF-16le):crlf:utf8", $qfn_out)
   or die("Can't open \"$qfn_out\": $!\n");

while (<$fh_in>) {
   next if $. == 1 && /MyHeader/;
   print($fh_out $_)
      or die("Can't write to \"$qfn_out\": $!");
}

close($fh_in);
close($fh_out) or die("Can't write to \"$qfn_out\": $!");

rename($qfn_out, $qfn_in)
   or die("Can't rename: $!\n");

:perlio:utf8是当时存在的错误的解决方法。)