File Coverage

blib/lib/App/CSVUtils/csv_select_rows.pm
Criterion Covered Total %
statement 11 11 100.0
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 15 15 100.0


line stmt bran cond sub pod time code
1             package App::CSVUtils::csv_select_rows;
2              
3 1     1   4572 use 5.010001;
  1         3  
4 1     1   5 use strict;
  1         2  
  1         21  
5 1     1   5 use warnings;
  1         2  
  1         75  
6              
7             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
8             our $DATE = '2023-03-31'; # DATE
9             our $DIST = 'App-CSVUtils'; # DIST
10             our $VERSION = '1.023'; # VERSION
11              
12 1         506 use App::CSVUtils qw(
13             gen_csv_util
14             compile_eval_code
15 1     1   6 );
  1         1  
16              
17             gen_csv_util(
18             name => 'csv_select_rows',
19             summary => 'Select (only output) rows based on a specification of row numbers (e.g. 2-5,7)',
20             description => <<'_',
21              
22             To select rows by Perl code, see <prog:csv-grep>.
23              
24             _
25             add_args => {
26             rownum_spec => {
27             schema => 'str*',
28             summary => 'Row number (e.g. 1 for first data row), '.
29             'range (3-7), or comma-separated list of such (3-7,10,20-23)',
30             req => 1,
31             pos => 1,
32             },
33             },
34             tags => ['category:filtering'],
35              
36             examples => [
37             {
38             summary => 'Only show rows 1-3 (first to third data rows)',
39             argv => ['file.csv', '1-3'],
40             test => 0,
41             'x.doc.show_result' => 0,
42             },
43             ],
44             links => [
45             {url=>'prog:csvgrep'},
46             ],
47              
48             on_input_header_row => sub {
49             my $r = shift;
50              
51             my $spec = $r->{util_args}{rownum_spec};
52             my @codestr;
53             for my $spec_item (split /\s*,\s*/, $spec) {
54             if ($spec_item =~ /\A\d+\z/) {
55             push @codestr, "(\$main::i == $spec_item)";
56             } elsif ($spec_item =~ /\A(\d+)\s*-\s*(\d+)\z/) {
57             push @codestr, "(\$main::i >= $1 && \$main::i <= $2)";
58             } else {
59             die [400, "Invalid rownum specification '$spec_item'"];
60             }
61             }
62              
63             # we add the following keys to the stash
64             $r->{code} = compile_eval_code(join(" || ", @codestr), 'from rownum_spec');
65             },
66              
67             on_input_data_row => sub {
68             my $r = shift;
69              
70             local $main::i = $r->{input_data_rownum};
71             $r->{code_print_row}->($r->{input_row}) if $r->{code}->();
72             },
73             );
74              
75             1;
76             # ABSTRACT: Select (only output) rows based on a specification of row numbers (e.g. 2-5,7)
77              
78             __END__
79              
80             =pod
81              
82             =encoding UTF-8
83              
84             =head1 NAME
85              
86             App::CSVUtils::csv_select_rows - Select (only output) rows based on a specification of row numbers (e.g. 2-5,7)
87              
88             =head1 VERSION
89              
90             This document describes version 1.023 of App::CSVUtils::csv_select_rows (from Perl distribution App-CSVUtils), released on 2023-03-31.
91              
92             =head1 FUNCTIONS
93              
94              
95             =head2 csv_select_rows
96              
97             Usage:
98              
99             csv_select_rows(%args) -> [$status_code, $reason, $payload, \%result_meta]
100              
101             Select (only output) rows based on a specification of row numbers (e.g. 2-5,7).
102              
103             Examples:
104              
105             =over
106              
107             =item * Only show rows 1-3 (first to third data rows):
108              
109             csv_select_rows(input_filename => "file.csv", rownum_spec => "1-3");
110              
111             =back
112              
113             To select rows by Perl code, see L<csv-grep>.
114              
115             This function is not exported.
116              
117             Arguments ('*' denotes required arguments):
118              
119             =over 4
120              
121             =item * B<inplace> => I<true>
122              
123             Output to the same file as input.
124              
125             Normally, you output to a different file than input. If you try to output to the
126             same file (C<-o INPUT.csv -O>) you will clobber the input file; thus the utility
127             prevents you from doing it. However, with this C<--inplace> option, you can
128             output to the same file. Like perl's C<-i> option, this will first output to a
129             temporary file in the same directory as the input file then rename to the final
130             file at the end. You cannot specify output file (C<-o>) when using this option,
131             but you can specify backup extension with C<-b> option.
132              
133             Some caveats:
134              
135             =over
136              
137             =item * if input file is a symbolic link, it will be replaced with a regular file;
138              
139             =item * renaming (implemented using C<rename()>) can fail if input filename is too long;
140              
141             =item * value specified in C<-b> is currently not checked for acceptable characters;
142              
143             =item * things can also fail if permissions are restrictive;
144              
145             =back
146              
147             =item * B<inplace_backup_ext> => I<str> (default: "")
148              
149             Extension to add for backup of input file.
150              
151             In inplace mode (C<--inplace>), if this option is set to a non-empty string, will
152             rename the input file using this extension as a backup. The old existing backup
153             will be overwritten, if any.
154              
155             =item * B<input_escape_char> => I<str>
156              
157             Specify character to escape value in field in input CSV, will be passed to Text::CSV_XS.
158              
159             Defaults to C<\\> (backslash). Overrides C<--input-tsv> option.
160              
161             =item * B<input_filename> => I<filename> (default: "-")
162              
163             Input CSV file.
164              
165             Use C<-> to read from stdin.
166              
167             Encoding of input file is assumed to be UTF-8.
168              
169             =item * B<input_header> => I<bool> (default: 1)
170              
171             Specify whether input CSV has a header row.
172              
173             By default, the first row of the input CSV will be assumed to contain field
174             names (and the second row contains the first data row). When you declare that
175             input CSV does not have header row (C<--no-input-header>), the first row of the
176             CSV is assumed to contain the first data row. Fields will be named C<field1>,
177             C<field2>, and so on.
178              
179             =item * B<input_quote_char> => I<str>
180              
181             Specify field quote character in input CSV, will be passed to Text::CSV_XS.
182              
183             Defaults to C<"> (double quote). Overrides C<--input-tsv> option.
184              
185             =item * B<input_sep_char> => I<str>
186              
187             Specify field separator character in input CSV, will be passed to Text::CSV_XS.
188              
189             Defaults to C<,> (comma). Overrides C<--input-tsv> option.
190              
191             =item * B<input_tsv> => I<true>
192              
193             Inform that input file is in TSV (tab-separated) format instead of CSV.
194              
195             Overriden by C<--input-sep-char>, C<--input-quote-char>, C<--input-escape-char>
196             options. If one of those options is specified, then C<--input-tsv> will be
197             ignored.
198              
199             =item * B<output_always_quote> => I<bool> (default: 0)
200              
201             Whether to always quote values.
202              
203             When set to false (the default), values are quoted only when necessary:
204              
205             field1,field2,"field three contains comma (,)",field4
206              
207             When set to true, then all values will be quoted:
208              
209             "field1","field2","field three contains comma (,)","field4"
210              
211             =item * B<output_escape_char> => I<str>
212              
213             Specify character to escape value in field in output CSV, will be passed to Text::CSV_XS.
214              
215             This is like C<--input-escape-char> option but for output instead of input.
216              
217             Defaults to C<\\> (backslash). Overrides C<--output-tsv> option.
218              
219             =item * B<output_filename> => I<filename>
220              
221             Output filename.
222              
223             Use C<-> to output to stdout (the default if you don't specify this option).
224              
225             Encoding of output file is assumed to be UTF-8.
226              
227             =item * B<output_header> => I<bool>
228              
229             Whether output CSV should have a header row.
230              
231             By default, a header row will be output I<if> input CSV has header row. Under
232             C<--output-header>, a header row will be output even if input CSV does not have
233             header row (value will be something like "col0,col1,..."). Under
234             C<--no-output-header>, header row will I<not> be printed even if input CSV has
235             header row. So this option can be used to unconditionally add or remove header
236             row.
237              
238             =item * B<output_quote_char> => I<str>
239              
240             Specify field quote character in output CSV, will be passed to Text::CSV_XS.
241              
242             This is like C<--input-quote-char> option but for output instead of input.
243              
244             Defaults to C<"> (double quote). Overrides C<--output-tsv> option.
245              
246             =item * B<output_quote_empty> => I<bool> (default: 0)
247              
248             Whether to quote empty values.
249              
250             When set to false (the default), empty values are not quoted:
251              
252             field1,field2,,field4
253              
254             When set to true, then empty values will be quoted:
255              
256             field1,field2,"",field4
257              
258             =item * B<output_sep_char> => I<str>
259              
260             Specify field separator character in output CSV, will be passed to Text::CSV_XS.
261              
262             This is like C<--input-sep-char> option but for output instead of input.
263              
264             Defaults to C<,> (comma). Overrides C<--output-tsv> option.
265              
266             =item * B<output_tsv> => I<bool>
267              
268             Inform that output file is TSV (tab-separated) format instead of CSV.
269              
270             This is like C<--input-tsv> option but for output instead of input.
271              
272             Overriden by C<--output-sep-char>, C<--output-quote-char>, C<--output-escape-char>
273             options. If one of those options is specified, then C<--output-tsv> will be
274             ignored.
275              
276             =item * B<overwrite> => I<bool>
277              
278             Whether to override existing output file.
279              
280             =item * B<rownum_spec>* => I<str>
281              
282             Row number (e.g. 1 for first data row), range (3-7), or comma-separated list of such (3-7,10,20-23).
283              
284              
285             =back
286              
287             Returns an enveloped result (an array).
288              
289             First element ($status_code) is an integer containing HTTP-like status code
290             (200 means OK, 4xx caller error, 5xx function error). Second element
291             ($reason) is a string containing error message, or something like "OK" if status is
292             200. Third element ($payload) is the actual result, but usually not present when enveloped result is an error response ($status_code is not 2xx). Fourth
293             element (%result_meta) is called result metadata and is optional, a hash
294             that contains extra information, much like how HTTP response headers provide additional metadata.
295              
296             Return value: (any)
297              
298             =head1 HOMEPAGE
299              
300             Please visit the project's homepage at L<https://metacpan.org/release/App-CSVUtils>.
301              
302             =head1 SOURCE
303              
304             Source repository is at L<https://github.com/perlancar/perl-App-CSVUtils>.
305              
306             =head1 AUTHOR
307              
308             perlancar <perlancar@cpan.org>
309              
310             =head1 CONTRIBUTING
311              
312              
313             To contribute, you can send patches by email/via RT, or send pull requests on
314             GitHub.
315              
316             Most of the time, you don't need to build the distribution yourself. You can
317             simply modify the code, then test via:
318              
319             % prove -l
320              
321             If you want to build the distribution (e.g. to try to install it locally on your
322             system), you can install L<Dist::Zilla>,
323             L<Dist::Zilla::PluginBundle::Author::PERLANCAR>,
324             L<Pod::Weaver::PluginBundle::Author::PERLANCAR>, and sometimes one or two other
325             Dist::Zilla- and/or Pod::Weaver plugins. Any additional steps required beyond
326             that are considered a bug and can be reported to me.
327              
328             =head1 COPYRIGHT AND LICENSE
329              
330             This software is copyright (c) 2023, 2022, 2021, 2020, 2019, 2018, 2017, 2016 by perlancar <perlancar@cpan.org>.
331              
332             This is free software; you can redistribute it and/or modify it under
333             the same terms as the Perl 5 programming language system itself.
334              
335             =head1 BUGS
336              
337             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=App-CSVUtils>
338              
339             When submitting a bug or request, please include a test-file or a
340             patch to an existing test-file that illustrates the bug or desired
341             feature.
342              
343             =cut