Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Re: Modifying CSV File

by Tux (Canon)
on Jun 17, 2015 at 10:09 UTC ( [id://1130781]=note: print w/replies, xml ) Need Help??


in reply to Modifying CSV File

I also am very curious what you did to make Text::CSV_XS be 4 time slower than your code.

For what it is worth, I tried to come up with the most efficient Text::CSV_XS code to attack your situation, only to find there was a bug in the module, which is now fixed in version 1.19. The code would be something like this:

use 5.20.0; use warnings; use Text::CSV_XS; my $csv = Text::CSV_XS->new ({ binary => 1, auto_diag => 1, keep_meta_info => 11, }); my $row = $csv->getline (*DATA); # get the first line to count number +of fields my @row = @$row; $csv->bind_columns (\(@row)); # speed up all remaining fetches do { $row[$_] =~ tr{,}{-} for grep { $csv->is_quoted ($_) } 0..$#row; $csv->say (*STDOUT, \@row); } while ($csv->getline (*DATA)); __END__ 65722417,"1193,1",7980,1133566,4169735,035,FEDERAL UNIVERSAL SERVICE F +UND,0.12998 65722417,"1193,1",1012,1132900,4150053,C2,Carrier Cost Recovery Fee,0. +0273
$ perl test.pl 65722417,"1193-1",7980,1133566,4169735,035,"FEDERAL UNIVERSAL SERVICE +FUND",0.12998 65722417,"1193-1",1012,1132900,4150053,C2,"Carrier Cost Recovery Fee", +0.0273

Not being dynamic, not forcing fields with a space to be quoted, and using a file instead of data, that would be:

use 5.20.0; use warnings; use Text::CSV_XS; my $csv = Text::CSV_XS->new ({ binary => 1, auto_diag => 1, quote_space => 0, keep_meta_info => 11, }); open my $io, "<", "test.csv"; my @row = ("") x 8; # The CSV has 8 columns $csv->bind_columns (\(@row)); while ($csv->getline ($io)) { $row[$_] =~ tr{,}{-} for grep { $csv->is_quoted ($_) } 0..$#row; $csv->say (*STDOUT, \@row); }

I'd be very interested in how much worse that performs on big datasets over your code.


Enjoy, Have FUN! H.Merijn

Replies are listed 'Best First'.
Re^2: Modifying CSV File
by roho (Bishop) on Jun 17, 2015 at 19:53 UTC
    I used your code and it took 82 seconds to process app 1.4 million records. My code processes the same records in 17 seconds. Also, since I am using Perl 5.8.9 I replaced "say" with "print", which worked, however the output did not have newlines (i.e., the output was one long record). I tried adding "\n" as a third argument to $csv->print but it gave me an error, so I'm not sure how to get newlines in my output. Is that related to the bug you found?

    "Its not how hard you work, its how much you get done."

      the say method I used in the code is the say method of Text::CSV_XS and does not require a recent perl to do what it is supposed to do. It acts as if you have a second CSV objects that has eol => "\r\n". This will work back to perl-5.6.1. If both your input and output documents have the same line ending, set it in the constructor and use print instead.

      Can you provide us with sample data and the simple code you use? If your data is indeed (very) simple and has no embedded newlines or escapes, then your code could be optimized to skip all situations that Text::CSV_XS has to consider. In that case, Text::CSV::Easy_XS or Text::CSV::Easy_PP might be of interest, as they do not allow altering the settings like separation character or quotation. They however have no means to check if the original field was quoted, which was what you stated as a precondition for your request. Text::CSV_XS might also be faster if you drop that requirement and/or if you know in advance which columns are object to the substitution.

      If you are not interested in keeping the original quotation, and you only want to replace the comma in column 2 you can gain a lot. As an example test, I created a 1_400_000 line file with just the two lines from your data example and have two tests. The first keeps original quotation and replaces , to - in all quoted columns. The second ignores all quotation requirements and replaces all , with - just in the second column, still generationg correct CSV data:

      use 5.20.0; use warnings; use Text::CSV_XS; my $csv = Text::CSV_XS->new ({ binary => 1, auto_diag => 1, quote_space => 0, keep_meta_info => 11, }); open my $io, "<", "test.csv"; open my $oh, ">", "out.csv"; my @row = ("") x 8; # The CSV has 8 columns $csv->bind_columns (\(@row)); while ($csv->getline ($io)) { $row[$_] =~ tr{,}{-} for grep { $csv->is_quoted ($_) } 0..$#row; $csv->say ($oh, \@row); }
      $ time perl test.pl 20.120u 0.156s 0:20.48 98.9% 0+0k 13888+214656io 0pf+0w
      use 5.20.0; use warnings; use Text::CSV_XS; my $csv = Text::CSV_XS->new ({ binary => 1, auto_diag => 1, eol => "\n", }); open my $io, "<", "test.csv"; open my $oh, ">", "out.csv"; my @row = ("") x 8; $csv->bind_columns (\(@row)); while ($csv->getline ($io)) { $row[1] =~ tr{,}{-}; $csv->print ($oh, \@row); }
      $ time perl test2.pl 5.564u 0.144s 0:05.90 96.6% 0+0k 16+214656io 0pf+0w

      That is 3.6 times faster. YMMV


      Enjoy, Have FUN! H.Merijn
        Here is the code I am using (with thanks to AnomalousMonk for the regex):

        while (<$fh1>) { chomp; next if $_ eq ''; s{ ("[^"]+") }{ (my $one = $1) =~ s{,}{-}xmsg; $one =~ s{"}{}g; $o +ne; }xmsge; print $_, "\n"; }

        The test file you made should be sufficient because the only thing I am changing is the comma to a dash and removing the quotes from the one column in question.

        "Its not how hard you work, its how much you get done."

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://1130781]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others lurking in the Monastery: (5)
As of 2024-04-20 00:31 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found