File Coverage

blib/lib/Text/CSV/Easy_PP.pm
Criterion Covered Total %
statement 36 38 94.7
branch 10 20 50.0
condition 3 9 33.3
subroutine 7 7 100.0
pod 2 2 100.0
total 58 76 76.3


line stmt bran cond sub pod time code
1             package Text::CSV::Easy_PP;
2 3     3   21232 use 5.010;
  3         6  
3 3     3   13 use strict;
  3         4  
  3         51  
4 3     3   8 use warnings FATAL => 'all';
  3         3  
  3         82  
5              
6 3     3   9 use Carp;
  3         2  
  3         222  
7 3     3   12 use Exporter qw(import);
  3         3  
  3         1771  
8              
9             our @EXPORT_OK = qw(csv_build csv_parse);
10              
11             =head1 NAME
12              
13             Text::CSV::Easy_PP - Easy CSV parsing and building implemented in PurePerl
14              
15             =head1 SYNOPSIS
16              
17             use Text::CSV::Easy_PP qw(csv_build csv_parse);
18              
19             my @fields = csv_parse($string);
20             my $string = csv_build(@fields);
21              
22             =head1 DESCRIPTION
23              
24             Text::CSV::Easy_PP is a simple module for parsing and building CSV lines. This module is written in PurePerl. For a faster alternative, see L. Either way, you should just be able to use L directly and it will determine which version to use.
25              
26             This module conforms to RFC 4180 (L) for both parsing and building of CSV lines.
27              
28             =over 4
29              
30             =item 1. Use commas to separate fields. Spaces will be considered part of the field.
31              
32             abc,def, ghi => ( 'abc', 'def', ' ghi' )
33              
34             =item 2. You may enclose fields in quotes.
35              
36             "abc","def" => ( 'abc', 'def' )
37              
38             =item 3. If your field contains a line break, a comma, or a quote, you need to enclose it in quotes. A quote should be escaped with another quote.
39              
40             "a,b","a\nb","a""b" => ( 'a,b', "a\nb", 'a"b' )
41              
42             =item 4. A trailing newline is acceptable (both LF and CRLF).
43              
44             abc,def\n => ( 'abc', 'def' )
45             abc,def\r\n => ( 'abc', 'def' )
46              
47             =back
48              
49             When building a string using csv_build, all non-numeric strings will always be enclosed in quotes.
50              
51             =head1 SUBROUTINES
52              
53             =head2 csv_build( List @fields ) : Str
54              
55             Takes a list of fields and will generate a CSV string. This subroutine will raise an exception if any errors occur.
56              
57             =cut
58              
59             sub csv_build {
60 1     1 1 3 my @fields = @_;
61             return join ',', map {
62 1 50       12 if ( !defined )
  2 50       6  
63             {
64 0         0 '';
65             }
66             elsif (/^\d+$/) {
67 0         0 $_;
68             }
69             else {
70 2         4 ( my $str = $_ ) =~ s/"/""/g;
71 2         8 qq{"$str"};
72             }
73             } @fields;
74             }
75              
76             =head2 csv_parse( Str $string ) : List[Str]
77              
78             Parses a CSV string. Returns a list of fields it found. This subroutine will raise an exception if a string could not be properly parsed.
79              
80             =cut
81              
82             sub csv_parse {
83 1     1 1 2 my ($str) = @_;
84              
85 1 50       3 return () unless $str;
86              
87 1         1 my $last_pos = 0;
88              
89 1         1 my @fields;
90 1         7 while (
91             $str =~ / (?:^|,)
92             (?: "" # don't want a capture group here
93             | "(.*?)(?
94             | ([^",\r\n]*) # try to match an unquoted field
95             )
96             (?:\r?\n(?=$)|) # allow a trailing newline only
97             (?=,|$) /xsg
98             )
99             {
100 2   33     6 my $field = $1 || $2;
101 2         3 my $match = $&;
102              
103             # is the field a numeric 0.
104 2 50 33     10 if ( defined($field) && $field =~ /^0+$/ ) {
105              
106             # don't do anything.
107             }
108             else {
109              
110             # if we don't have a value, we have either an undef or an empty string.
111             # "" will be an empty string, otherwise it should be undef.
112 2 0 33     4 $field ||= ( $match =~ /^,?""(?:\r?\n)?$/ ? "" : undef );
113             }
114              
115             # track the pos($str) to ensure each field happends immediately after the
116             # previous match. also, account for a leading comma when $last_pos != 0
117 2 100       7 croak("invalid line: $str")
    50          
118             if pos($str) > $last_pos + length($match) + ( $last_pos != 0 ? 1 : 0 );
119              
120 2         2 $last_pos = pos($str);
121              
122 2 50       3 if ($field) {
123 2 50       4 croak("quote is not properly escaped")
124             if ( $field =~ /(?
125              
126             # unescape the quotes.
127 2         2 $field =~ s/""/"/g;
128             }
129 2         7 push @fields, $field;
130             }
131              
132 1 50       8 croak("invalid line: $str") if $last_pos != length($str);
133              
134 1         8 return @fields;
135             }
136              
137             1;
138              
139             =head1 SEE ALSO
140              
141             =over 4
142              
143             =item L
144              
145             =item L
146              
147             =item L
148              
149             =back
150              
151             =head1 AUTHOR
152              
153             Thomas Peters, Eweters@me.comE
154              
155             =head1 COPYRIGHT AND LICENSE
156              
157             Copyright (C) 2013 by Thomas Peters
158              
159             This library is free software; you can redistribute it and/or modify
160             it under the same terms as Perl itself, either Perl version 5.12.4 or,
161             at your option, any later version of Perl 5 you may have available.
162              
163             =cut