Text :: CSV_XSを使用して、csv形式の文字列をディスクに書き込まずに解析できますか?


8

(APIを使用して)ベンダーから「csvファイル」を取得していますが、彼らがしていることはすべてを彼らの応答に吹き込んでいるだけです。もちろん、それらの厄介な人間の一部がデータを入力して改行のような「機能」を付けたことを除いて、それは重大な問題ではありません。私が今行っているのは、生データ用のファイルを作成し、それを再度開いてデータを読み取ることです。

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(複数行の文字列)からデータをファイルのように読み取ることができるようにすべきです。しかし、私はこれをどのように行うかについて完全な空白を描いています。ポインタをいただければ幸いです。ありがとう、ポール

回答:


6

あなたは文字列ファイルハンドルを使うことができます:

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

3
これは、Perlでの私のお気に入りのトリックの1つであり、私はそれについて「効果的なPerlプログラミング」でかなり書きます。多くのものをファイルハンドルとして扱うことは、より簡単で使い慣れたインターフェースを持っていることを意味します。それはまた逆になります。ファイルハンドルに書き込むことはできますが、文字列で表示させることができます。
brian d foy

3
はい、いいです。私もそれを使用しています。問題が発生しないように、適切なファイルハンドルではないことを忘れないでください。たとえば、この投稿を参照してください。
zdim

1
有難う御座います!それはまさに私が探していたものでしたが、かなり得られませんでした。私が試した組み合わせを正確に思い出すことはできなくなりましたが、明らかに近いですが、構文が正しくありませんでした。
ポールRN

5

はい、機能インターフェースを介して、文字列に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を参照)。


3

この例をMojo :: UserAgentで作りました。CSV入力には、NYC Open Dataのさまざまなデータセットを使用しました。これは、Mojo Webクライアントの次の更新でも表示される予定です。

リクエストをすぐに作成せずにリクエストを作成すると、トランザクションオブジェクトが得られます$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];
            }
        };
    }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.