File Coverage

blib/lib/String/Scanf.pm
Criterion Covered Total %
statement 69 74 93.2
branch 42 48 87.5
condition n/a
subroutine 7 10 70.0
pod 0 5 0.0
total 118 137 86.1


line stmt bran cond sub pod time code
1             package String::Scanf;
2              
3 1     1   1850 use strict;
  1         4  
  1         115  
4              
5 1     1   9 use vars qw($VERSION @ISA @EXPORT);
  1         3  
  1         161  
6              
7             $VERSION = '2.1';
8              
9             require Exporter;
10             @ISA = qw(Exporter);
11             @EXPORT = qw(sscanf);
12              
13             =pod
14              
15             =head1 NAME
16              
17             String::Scanf - emulate sscanf() of the C library
18              
19             =head1 SYNOPSIS
20              
21             use String::Scanf; # imports sscanf()
22              
23             ($a, $b, $c, $d) = sscanf("%d+%d %f-%s", $input);
24             ($e, $f, $g, $h) = sscanf("%x %o %s:%3c"); # input defaults to $_
25              
26             $r = String::Scanf::format_to_re($f);
27              
28             or
29              
30             # works only for Perl 5.005 or later
31             use String::Scanf qw(); # import nothing
32              
33             my $s1 = String::Scanf->new("%d+%d %f-%s");
34             my $s2 = String::Scanf->new("%x %o %s:%3c");
35              
36             ($a, $b, $c, $d) = $s1->sscanf($input);
37             ($e, $f, $g, $h) = $s2->sscanf(); # input defaults to $_
38              
39             =head1 DESCRIPTION
40              
41             String::Scanf supports scanning strings for data using formats
42             similar to the libc/stdio sscanf().
43              
44             The supported sscanf() formats are as follows:
45              
46             =over 4
47              
48             =item %d
49              
50             Decimal integer, with optional plus or minus sign.
51              
52             =item %u
53              
54             Decimal unsigned integer, with optional plus sign.
55              
56             =item %x
57              
58             Hexadecimal unsigned integer, with optional "0x" or "0x" in front.
59              
60             =item %o
61              
62             Octal unsigned integer.
63              
64             =item %e %f %g
65              
66             (The [efg] work identically.)
67              
68             Decimal floating point number, with optional plus or minus sign,
69             in any of these formats:
70              
71             1
72             1.
73             1.23
74             .23
75             1e45
76             1.e45
77             1.23e45
78             .23e45
79              
80             The exponent has an optional plus or minus sign, and the C may also be C.
81              
82             The various borderline cases like C and C are not recognized.
83              
84             =item %s
85              
86             A non-whitespace string.
87              
88             =item %c
89              
90             A string of characters. An array reference is returned containing
91             the numerical values of the characters.
92              
93             =item %%
94              
95             A literal C<%>.
96              
97             =back
98              
99             The sscanf() formats [pnSC] are not supported.
100              
101             The C<%s> and C<%c> have an optional maximum width, e.g. C<%4s>,
102             in which case at most so many characters are consumed (but fewer
103             characters are also accecpted).
104              
105             The numeric formats may also have such a width but it is ignored.
106              
107             The numeric formats may have C<[hl]> before the main option, e.g. C<%hd>,
108             but since such widths have no meaning in Perl, they are ignored.
109              
110             Non-format parts of the parameter string are matched literally
111             (e.g. C<:> matches as C<:>),
112             expect that any whitespace is matched as any whitespace
113             (e.g. C< > matches as C<\s+>).
114              
115             =head1 WARNING
116              
117             The numeric formats match only something that looks like a number,
118             they do not care whether it fits into the numbers of Perl. In other
119             words, C<123e456789> is valid for C, but quite probably it
120             won't fit into your Perl's numbers. Consider using the various
121             Math::* modules instead.
122              
123             =head1 AUTHOR, COPYRIGHT AND LICENSE
124              
125             Jarkko Hietaniemi
126              
127             Copyright (c) 2002,2004 Jarkko Hietaniemi. All rights reserved.
128              
129             This program is free software; you can redistribute it and/or modify
130             it under the same terms as Perl itself.
131              
132             =cut
133              
134 1     1   7 use Carp;
  1         6  
  1         2609  
135              
136             sub _format_to_re {
137 120     120   148 my $format = shift;
138              
139 120         137 my $re = '';
140 120         132 my $ix = 0;
141 120         136 my @fmt;
142             my @reo;
143 120         121 my $dx = '\d+(?:_\d+)*';
144              
145 120         590 while ($format =~
146             /(%(?:(?:(\d+)\$)?(\d*)([hl]?[diuoxefg]|[pnsScC%]))|%(\d*)(\[.+?\])|(.+?))/g) {
147 213 100       374 if (defined $2) { # Reordering.
148 2         6 $reo[$ix] = $2 - 1;
149             } else {
150 211         290 $reo[$ix] = $ix;
151             }
152 213 50       419 if (defined $1) {
153 213 100       406 if (defined $4) {
    100          
    50          
154 175         147 my $e;
155 175         486 my ($w, $f) = ($3, $4);
156 175         319 $f =~ s/^[hl]//;
157 175 50       845 if ($f =~ /^[pnSC]$/) {
    100          
    100          
    100          
    100          
    100          
    100          
    50          
158 0         0 croak "'$f' not supported";
159             } elsif ($f =~ /^[di]$/) {
160 28         39 $e = "[-+]?$dx";
161             } elsif ($f eq 'x') {
162 12         17 $e = '(?:0[xX])?[0-9A-Fa-f]+(?:_[0-9A-Fa-f]+)*';
163             } elsif ($f eq 'o') {
164 8         11 $e = '[0-7]+(?:_[0-7]+)*';
165             } elsif ($f =~ /^[efg]$/) {
166 102         263 $e = "[-+]?(?:(?:$dx(?:\\.(?:$dx)?)?|\\.$dx)(?:[eE][-+]?$dx)?)";
167             } elsif ($f eq 'u') {
168 8         14 $e = "\\+?$dx";
169             } elsif ($f eq 's') {
170 16 100       34 $e = $w ? "\\S{0,$w}" : "\\S*";
171             } elsif ($f eq 'c') {
172 1 50       4 $e = $w ? ".{0,$w}" : ".*";
173             }
174 175 100       378 if ($f !~ /^[cC%]$/) {
175 174         187 $re .= '\s*';
176             }
177 175         231 $re .= "($e)";
178 175         791 $fmt[$ix++] = $f;
179             } elsif (defined $6) { # [...]
180 1 50       6 $re .= $5 ? "(${6}{0,$5})" : "($6+)";
181 1         6 $fmt[$ix++] = '[';
182             } elsif (defined $7) { # Literal.
183 37         48 my $lit = $7;
184 37 100       96 if ($lit =~ /^\s+$/) {
185 21         93 $re .= '\s+';
186             } else {
187 16         61 $lit =~ s/(\W)/\\$1/g;
188 16         83 $re .= $lit;
189             }
190             }
191             }
192             }
193              
194 120         163 $re =~ s/\\s\*\\s\+/\\s+/g;
195 120         173 $re =~ s/\\s\+\\s\*/\\s+/g;
196              
197 120         746 return ($re, \@fmt, \@reo);
198             }
199              
200             sub format_to_re {
201 0     0 0 0 my ($re) = _format_to_re $_[0];
202 0         0 return $re;
203             }
204              
205             sub _match {
206 121     121   294 my ($format, $re, $fmt, $reo, $data) = @_;
207 121         1954 my @matches = ($data =~ /$re/);
208              
209 121         204 my $ix;
210 121         349 for ($ix = 0; $ix < @matches; $ix++) {
211 177 100       570 if ($fmt->[$ix] eq 'c') {
    100          
212 1         7 $matches[$ix] = [ map { ord } split //, $matches[$ix] ];
  2         6  
213             } elsif ($fmt->[$ix] =~ /^[diuoxefg]$/) {
214 159         219 $matches[$ix] =~ tr/_//d;
215             }
216 177 100       667 if ($fmt->[$ix] eq 'x') {
    100          
217 12         20 $matches[$ix] =~ s/^0[xX]//;
218 12         33 $matches[$ix] = hex $matches[$ix];
219             } elsif ($fmt->[$ix] eq 'o') {
220 8         21 $matches[$ix] = oct $matches[$ix];
221             }
222             }
223 121         446 @matches = @matches[@$reo];
224              
225 121         409 return @matches;
226             }
227              
228             sub new {
229 1     1 0 63 require 5.005; sub qr {}
  0     0 0 0  
230 1         5 my ($class, $format) = @_;
231 1         3 my ($re, $fmt, $reo) = _format_to_re $format;
232 1         41 bless [ $format, qr/$re/, $fmt, $reo ], $class;
233             }
234              
235             sub format {
236 0     0 0 0 $_[0]->[0];
237             }
238              
239             sub sscanf {
240 121     121 0 3003 my $self = shift;
241 121 100       267 my $data = @_ ? shift : $_;
242 121 100       227 if (ref $self) {
243 2         2 return _match(@{ $self }, $data);
  2         13  
244             }
245 119         175 _match($self, _format_to_re($self), $data);
246             }
247              
248             1;