我可以使用 Text::CSV_XS 来解析 csv 格式的字符串而不将其写入磁盘吗?

Can I use Text::CSV_XS to parse a csv-format string without writing it to disk?

我从供应商那里得到了一个 "csv file"(使用他们的 API),但他们所做的只是把整个事情都吐到他们的回复中。当然,除了一些讨厌的人输入数据并像换行符一样输入 "features" 之外,这不会是一个重大问题。我现在正在做的是为原始数据创建一个文件,然后重新打开它来读取数据:

open RAW, ">", "$rawfile" or die "ERROR: Could not open $rawfile for write: $! \n";
print RAW $response->content;
close RAW;

my $csv = Text::CSV_XS->new({ binary=>1,always_quote=>1,eol=>$/ });
open my $fh, "<", "$rawfile" or die "ERROR: Could not open $rawfile for read: $! \n";

while ( $line = $csv->getline ($fh) ) { ...

不知何故,这似乎...不雅。看来我应该能够从 $response->content(多行字符串)中读取数据,就好像它是一个文件一样。但我对如何做到这一点一无所知。 一个指针将不胜感激。 谢谢, 保罗

您可以使用字符串文件句柄:

my $data = $response->content;
open my $fh, "<", $data or croak "unable to open string filehandle : $!";
my $csv = Text::CSV_XS->new({ binary=>1,always_quote=>1,eol=>$/ });
while ( $line = $csv->getline ($fh) ) { ... }

是的,您可以通过其功能接口在字符串上使用 Text::CSV_XS

use warnings;
use strict;
use feature 'say';

use Text::CSV_XS qw(csv);  # must use _XS version

my $csv = qq(a,line\nand,another);

my $aoa = csv(in => $csv) 
    or die Text::CSV->error_diag; 

say "@$_" for @aoa;    

请注意,这确实需要 Text::CSV_XS(通常 Text::CSV 有效,但不适用于此)。

我不知道为什么这在 OO 界面中不可用(或者可能是但没有记录)。


虽然上面直接按照要求解析字符串,但也可以通过在获取内容时将内容直接写入文件来减少示例中的 "inelegant" 方面,大多数库都支持 :content_file LWP::UserAgent::get 方法中的选项。

我还要注意,大多数时候您希望图书馆解码内容,因此 LWP::UA 使用 decoded_content(参见 HTTP::Response)。

我用 Mojo::UserAgent. For the CSV input I used various data sets from the NYC Open Data. This is also going to appear in the next update for Mojo Web Clients 编造了这个例子。

我没有立即发出请求就构建了请求,这给了我交易对象,$tx。然后我可以替换 read 事件,这样我就可以立即将行发送到 Text::CSV_XS:

#!perl

use v5.10;
use Mojo::UserAgent;

my $ua = Mojo::UserAgent->new;

my $url = ...;
my $tx = $ua->build_tx( GET => $url );

$tx->res->content->unsubscribe('read')->on(read => sub {
    state $csv = do {
        require Text::CSV_XS;
        Text::CSV_XS->new;
        };
    state $buffer;
    state $reader = do {
        open my $r, '<:encoding(UTF-8)', $buffer;
        $r;
        };

    my ($content, $bytes) = @_;
    $buffer .= $bytes;
    while (my $row = $csv->getline($reader) ) {
        say join ':', $row->@[2,4];
        }
    });

$tx = $ua->start($tx);

这不像我希望的那样好,因为所有数据仍然显示在缓冲区中。这稍微更有吸引力,但就我在评论中提到的方式而言,它很脆弱。我现在太懒了,无法让它变得更好,因为当您确定何时有足够的数据来处理记录时,它会很快变得毛茸茸。我的特定代码并不重要,因为当事务处理程序读取数据并将其传递到内容处理程序时,您可以随心所欲地做任何事情:

use v5.10;
use strict;
use warnings;
use feature qw(signatures);
no warnings qw(experimental::signatures);

use Mojo::UserAgent;

my $ua = Mojo::UserAgent->new;

my $url = ...;
my $tx = $ua->build_tx( GET => $url );

$tx->res->content
    ->unsubscribe('read')
    ->on( read => process_bytes_factory() );

$tx = $ua->start($tx);

sub process_bytes_factory {
    return sub ( $content, $bytes ) {
        state $csv = do {
            require Text::CSV_XS;
            Text::CSV_XS->new( { decode_utf8 => 1 } );
            };
        state $buffer = '';
        state $line_no = 0;

        $buffer .= $bytes;
        # fragile if the entire content does not end in a
        # newline (or whatever the line ending is)
        my $last_line_incomplete = $buffer !~ /\n\z/;

        # will not work if the format allows embedded newlines
        my @lines = split /\n/, $buffer;
        $buffer = pop @lines if $last_line_incomplete;

        foreach my $line ( @lines ) {
            my $status = $csv->parse($line);
            my @row = $csv->fields;
            say join ':', $line_no++, @row[2,4];
            }
        };
    }