1 package SL::Helper::Csv;
9 use Params::Validate qw(:all);
10 use Rose::Object::MakeMethods::Generic scalar => [ qw(
11 file encoding sep_char quote_char escape_char header header_acc class
12 numberformat dateformat _io _csv _objects _parsed _data _errors
20 my %params = validate(@_, {
21 sep_char => { default => ';' },
22 quote_char => { default => '"' },
23 escape_char => { default => '"' },
24 header => { type => ARRAYREF, optional => 1 },
25 header_acc => { type => HASHREF, optional => 1 },
32 my $self = bless {}, $class;
34 $self->$_($params{$_}) for keys %params;
36 $self->_io(IO::File->new);
37 $self->_csv(Text::CSV->new({
39 sep_char => $self->sep_char,
40 quote_char => $self->quote_char,
41 escape_char => $self->escape_char,
50 my ($self, %params) = @_;
53 return unless $self->_check_header;
54 return unless $self->_parse_data;
65 my ($self, %params) = @_;
66 croak 'no class given' unless $self->class;
67 croak 'must parse first' unless $self->_parsed;
69 $self->_make_objects unless $self->_objects;
70 return wantarray ? @{ $self->_objects } : $self->_objects;
80 my ($self, %params) = @_;
82 $self->encoding($self->_guess_encoding) if !$self->encoding;
84 $self->_io->open($self->file, '<' . $self->_encode_layer)
85 or die "could not open file " . $self->file;
91 my ($self, %params) = @_;
92 return $self->header if $self->header;
94 my $header = $self->_csv->getline($self->_io);
96 $self->header($header);
100 my ($self, %params) = @_;
103 $self->_csv->column_names(@{ $self->header });
106 my $row = $self->_csv->getline($self->_io);
107 last if $self->_csv->eof;
111 @hr{@{ $self->header }} = @$row;
115 $self->_csv->error_input,
116 $self->_csv->error_diag,
117 $self->_io->input_line_number,
122 $self->_data(\@data);
123 $self->_errors(\@errors);
130 ':encoding(' . $_[0]->encoding . ')';
134 my ($self, %params) = @_;
137 eval "require " . $self->class;
138 local $::myconfig{numberformat} = $self->numberformat if $self->numberformat;
139 local $::myconfig{dateformat} = $self->dateformat if $self->dateformat;
141 for my $line (@{ $self->_data }) {
142 push @objs, $self->class->new(
144 ($self->header_acc && $self->header_acc->{$_}) || $_ => $line->{$_}
145 } grep { $_ } keys %$line
149 $self->_objects(\@objs);
152 sub _guess_encoding {
166 SL::Helper::Csv - take care of csv file uploads
172 my $csv = SL::Helper::Csv->new(
173 file => \$::form->{upload_file},
174 encoding => 'utf-8', # undef means utf8
175 sep_char => ',', # default ';'
176 quote_char => ''', # default '"'
177 header => [qw(id text sellprice word)] # see later
178 header_acc => { sellprice => 'sellprice_as_number' }
179 class => 'SL::DB::CsvLine', # if present, map lines to this
182 my $status = $csv->parse;
183 my $hrefs = $csv->get_data;
184 my @objects = $scv->get_objects;
190 Text::CSV offeres already good functions to get lines out of a csv file, but in
191 most cases you will want those line to be parsed into hashes or even objects,
192 so this model just skips ahead and gives you objects.
194 Encoding autodetection is not easy, and should not be trusted. Try to avoid it if possible.
202 Standard constructor. You can use this to set most of the data.
206 Do the actual work. Will return true ($self actually) if success, undef if not.
210 Parse the data into objects and return those.
212 This method will return list or arrayref depending on context.
216 Returns an arrayref of the raw lines as hashrefs.
220 Return all errors that came up druing parsing. See error handling for detailed
231 The file which contents are to be read. Can be a name of a physical file or a
232 scalar ref for memory data.
236 Encoding of the CSV file. Note that this module does not do any encoding guessing.
237 Know what your data ist. Defaults to utf-8.
245 Same as in L<Text::CSV>
247 =item C<header> \@FIELDS
249 can be an array of columns, in this case the first line is not used as a
250 header. Empty header fields will be ignored in objects.
252 =item C<header_acc> \%ACCESSORS
254 May be used to map header fields to custom accessors. Example:
256 { listprice => listprice_as_number }
258 In this case C<listprice_as_number> will be used to read in values from the
263 If present, the line will be handed to the new sub of this class,
264 and the return value used instead of the line itself.
268 =head1 ERROR HANDLING
270 After parsing a file all errors will be accumulated into C<errors>.
272 Each entry is an arrayref with the following structure:
276 Text::CSV error code if present,
277 Text::CSV error diagnostics if present,
279 estimated line in file,
282 Note that the last entry can be off, but will give an estimate.
290 sep_char, quote_char, and escape_char are passed to Text::CSV on creation.
291 Changing them later has no effect currently.
295 Encoding errors are not dealt with properly.
299 Errors are not gathered.
305 Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>