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

问题描述 投票:3回答:3

我正在从供应商那里得到一个“ csv文件”(使用他们的API),但是他们所做的只是将整个内容喷入他们的响应中。除了有一些讨厌的人输入数据并放入诸如换行符之类的“功能”之外,这不会是一个大问题。我现在正在做的是为原始数据创建一个文件,然后重新打开它以读取数据:

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(多行字符串)中读取数据,就好像它是一个文件一样。但是我在如何做到这一点上完全空白。指针将不胜感激。谢谢,保罗

csv perl filehandle
3个回答
4
投票

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

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) ) { ... }

2
投票

是,您可以通过其功能接口在字符串上使用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界面中不可用(或者也许但没有记录)。


关于减轻“模糊”方面的注意,请注意,大多数库都允许您直接写入文件,例如:content_file方法中的LWP::UserAgent::get选项。

让我也注意到,大多数时候您希望库对内容进行解码,因此LWP::UA使用decoded_content(请参阅HTTP::Response)。


1
投票

我用Mojo::UserAgent编写了这个示例。对于CSV输入,我使用了NYC Open Data中的各种数据集。这也将出现在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];
            }
        };
    }
© www.soinside.com 2019 - 2024. All rights reserved.