File Coverage

blib/lib/WordLists/Parse/Simple.pm
Criterion Covered Total %
statement 88 93 94.6
branch 28 36 77.7
condition 13 20 65.0
subroutine 11 11 100.0
pod 3 4 75.0
total 143 164 87.2


line stmt bran cond sub pod time code
1             package WordLists::Parse::Simple;
2 7     7   2028 use strict;
  7         15  
  7         308  
3 7     7   35 use warnings;
  7         11  
  7         187  
4 7     7   5783 use IO::File;
  7         83120  
  7         1148  
5 7     7   3191 use WordLists::Common qw (@sDefaultAttList @sDefiningAttlist);
  7         23  
  7         1212  
6 7     7   37 use WordLists::Base;
  7         14  
  7         8979  
7             our $VERSION = $WordLists::Base::VERSION;
8            
9             my $canUseFileBOM=0;
10             eval { require File::BOM; File::BOM->import(); };
11             unless ($@)
12             {
13             $canUseFileBOM = 1;
14             }
15            
16             sub parse_string
17             {
18 31     31 1 5893 my ($self, $string, $args) = @_;
19 31         53 foreach (grep {defined $self->{$_};} qw(attlist field_sep header_marker))
  93         406  
20             {
21 91 100       609 $args->{$_} = $self->{$_} unless defined $args->{$_};
22             }
23 31 100       96 $args->{'line_sep'} = $self->_get_line_sep unless defined $args->{'line_sep'};
24 31         41 my @sAttList;
25 31 100       66 @sAttList = ($args->{'attlist'} ? @{$args->{'attlist'}} : @{$self->{'default_attlist'}});
  29         93  
  2         9  
26 31         49 my $LS = $args->{'line_sep'};
27 31         702 my @sLines = split (/$LS/,$string);
28 31         45 my @senseList;
29 31         58 foreach my $sLine (grep {m/\w/} @sLines) # todo: make this condition changeable
  37         303  
30             {
31            
32 36         140 chomp $sLine;
33 36         57 my $FS = $args->{'field_sep'};
34 36         497 my @sCols = split (/$FS/, $sLine);
35 36 100 100     637 if ($args->{'is_header'} or (!defined ($args->{'is_header'}) and $sLine =~ m/^$args->{'header_marker'}/))
      66        
36             {
37 9 100       64 $sCols[0] =~s/^$args->{'header_marker'}// unless $args->{'is_header'};
38 9         30 @sAttList = @sCols;
39 9         13 @{$self->{'attlist'}} = @sCols;
  9         35  
40 9         16 @{$args->{'attlist'}} = @sCols;
  9         43  
41             }
42             else
43             {
44 27         214 my %sAttr = ();
45 27         65 foreach (0..$#sAttList )
46             {
47 71 50       475 $sAttr{$sAttList[$_]} = $sCols[$_] if $sAttList[$_] =~ m/^\w+$/;
48 71 50       535 if ($sAttList[$_] =~ m/^(\w+)\[(\d+)\]$/)
49             {
50 0         0 $sAttr{$1}[$2] = $sCols[$_];
51             }
52             }
53 27         465 push @senseList, \%sAttr;
54             }
55             }
56 31         1032 return \@senseList;
57             }
58             sub new
59             {
60 4     4 0 50 my ($class, $args) = @_;
61 4         41 my $self = {
62             'field_sep' => "\t",
63             'default_attlist'=> [@sDefaultAttList],
64             'header_marker' => quotemeta '#*',
65             'line_sep' => \$/,
66             };
67 4         12 $self->{$_} = $args->{$_} foreach grep { defined $args->{$_}; }(qw(field_sep attlist default_attlist header_marker line_sep));
  20         48  
68 4         39 bless $self, $class;
69             }
70            
71             sub parse_file
72             {
73 3     3 1 1683 my ($self, $fn, $enc, $args) = @_;
74 3         6 my $fh;
75 3         7 my $structure = [];
76 3 100       40 if (defined $enc)
    50          
77             {
78 1         47 $fh = IO::File->new($fn, "<:encoding($enc)");
79             }
80             elsif ($canUseFileBOM)
81             {
82 0         0 $fh = IO::File->new($fn, "<:via(File::BOM)");
83             }
84             else
85             {
86 2         22 $fh = IO::File->new($fn, "<");
87             }
88 3 50       15813 if (defined $fh)
89             {
90 3         17 $structure= $self->parse_fh($fh, $args);
91 3         6 undef $fh;
92             }
93             else
94             {
95 0   0     0 $enc ||= 'undefined';
96 0         0 warn "Open $fn with encoding $enc failed!";
97             }
98 3         88 return $structure;
99             }
100             sub _get_line_sep
101             {
102 7     7   12 my $self = shift;
103 7 100       31 if (ref $self->{'line_sep'} eq ref \'')
    50          
104             {
105 6         9 return ${$self->{'line_sep'}};
  6         191  
106             }
107             elsif (!ref $self->{'line_sep'})
108             {
109 1         5 return $self->{'line_sep'};
110             }
111             else
112             {
113 0         0 return $/;
114             }
115             }
116             sub parse_fh
117             {
118 3     3 1 7 my ($self, $fh, $args) = @_;
119 3 50       13 $args = {} unless defined $args;
120 3         14 $self->{'attlist'} = $self->{'default_attlist'};
121 3         6 my $iLine=0;
122 3         6 my @senses;
123 3 100       16 $args->{'header_marker'} = $self->{'header_marker'} unless defined $args->{'header_marker'};
124 3         5 if (0)
125             {
126             local $/ = $/;
127             unless (ref $self->{'line_sep'} eq ref \'' and ${$self->{'line_sep'}} eq $/)
128             {
129             $/ = $self->_get_line_sep;
130             }
131             if (defined ($args->{'line_sep'}))
132             {
133             $/ = $args->{'line_sep'};
134             }
135             }
136 3         72 while (my $sLine = <$fh>)
137             {
138 24 50 66     175 if ($iLine == 0 and ($sLine=~ s/^\x{feff}// or $sLine=~ s/^\xef\xbb\xbf//))
      66        
139             {
140 3     1   60 binmode $fh, ':encoding(UTF-8)';
  1         11  
  1         2  
  1         8  
141             }
142 24         14871 my $senses_per_line =[];
143 24 100 100     165 if ($args->{'header_marker'} =~ m/^\d+$/ and $iLine == $args->{'header_marker'})
    100          
144             {
145 2         18 $senses_per_line = $self->parse_string($sLine, {%$args, is_header=>1}); # a header
146             }
147             elsif ($args->{'header_marker'} =~ m/^\d+$/)
148             {
149 6         35 $senses_per_line = $self->parse_string($sLine, {%$args, is_header=>0}); # not a header
150             }
151             else
152             {
153 16         41 $senses_per_line = $self->parse_string($sLine, $args); # could be a header
154             }
155 24 50 33     159 if (defined $senses_per_line and ref $senses_per_line eq ref [])
156             {
157 24         27 push @senses, $_ foreach @{$senses_per_line} ;
  24         144  
158             }
159 24         158 $iLine++;
160             }
161 3         12 return \@senses;
162             }
163             1;
164            
165             =pod
166            
167             =head1 NAME
168            
169             WordLists::Parse::Simple
170            
171             =head1 SYNOPSIS
172            
173             my $parser = WordLists::Parse::Simple->new;
174             my @senses = @{ $parser->parse_string('#*hw\tpos\tdef\nhead\tnoun\tnoggin') };
175            
176             =head1 DESCRIPTION
177            
178             This is a simple parser for CSV/TSV files. It doesn't do any quoted values or anything like that - the delimiter must simply never occur in the text.
179            
180             The parser aims to return each row as a hashref where the keys are the column names. It needs to be given information about how to identify the header, as there is no standardised way of representing a header. (The default is to treat lines beginning C<#*> as headers).
181            
182             If the parser is passed several rows, it will return an arrayref.
183            
184             =head1 OPTIONS
185            
186             On creation, a hashref may be passed with configuration options.
187            
188             =head1 METHODS
189            
190             =head3 parse_fh
191            
192             =head3 parse_file
193            
194             When the module is loaded, it checks if L can be used. If it can, then it will try to use it to guess the encoding when the user does not specify it.
195            
196             =head3 parse_string
197            
198             =head1 BUGS
199            
200             Please use the Github issues tracker.
201            
202             =head1 LICENSE
203            
204             Copyright 2011-2012 © Cambridge University Press. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
205            
206             =cut