| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package PDL::Char; |
|
2
|
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
420
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
27
|
|
|
4
|
1
|
|
|
1
|
|
4
|
use warnings; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
129
|
|
|
5
|
|
|
|
|
|
|
our @ISA = qw (PDL); |
|
6
|
1
|
|
|
1
|
|
5
|
use overload '""' => \&PDL::Char::string; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
9
|
|
|
7
|
|
|
|
|
|
|
|
|
8
|
|
|
|
1
|
|
|
sub import {} # override the PDL one to avoid the big import list |
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=head1 NAME |
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
PDL::Char -- PDL subclass which allows reading and writing of fixed-length character strings as byte PDLs |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
use PDL; |
|
17
|
|
|
|
|
|
|
use PDL::Char; |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
my $pchar = PDL::Char->new( [['abc', 'def', 'ghi'],['jkl', 'mno', 'pqr']] ); |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
$pchar->setstr(1,0,'foo'); |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
print $pchar; # 'string' bound to "", perl stringify function |
|
24
|
|
|
|
|
|
|
# Prints: |
|
25
|
|
|
|
|
|
|
# [ |
|
26
|
|
|
|
|
|
|
# ['abc' 'foo' 'ghi'] |
|
27
|
|
|
|
|
|
|
# ['jkl' 'mno' 'pqr'] |
|
28
|
|
|
|
|
|
|
# ] |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
print $pchar->atstr(2,0); |
|
31
|
|
|
|
|
|
|
# Prints: |
|
32
|
|
|
|
|
|
|
# ghi |
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
This subclass of PDL allows one to manipulate PDLs of 'byte' type as if they were made of fixed |
|
37
|
|
|
|
|
|
|
length strings, not just numbers. |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
This type of behavior is useful when you want to work with character grids. The indexing is done |
|
40
|
|
|
|
|
|
|
on a string level and not a character level for the 'setstr' and 'atstr' commands. |
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
This module is in particular useful for writing NetCDF files that include character data using the |
|
43
|
|
|
|
|
|
|
L module. |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
=head1 FUNCTIONS |
|
46
|
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
=head2 new |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
=for ref |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
Function to create a byte PDL from a string, list of strings, list of list of strings, etc. |
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
=for usage |
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
# create a new PDL::Char from a perl array of strings |
|
56
|
|
|
|
|
|
|
$strpdl = PDL::Char->new( ['abc', 'def', 'ghij'] ); |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
# Convert a PDL of type 'byte' to a PDL::Char |
|
59
|
|
|
|
|
|
|
$strpdl1 = PDL::Char->new (sequence (byte, 4, 5)+99); |
|
60
|
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=for example |
|
62
|
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
$pdlchar3d = PDL::Char->new([['abc','def','ghi'],['jkl', 'mno', 'pqr']]); |
|
64
|
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=cut |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
sub new { |
|
68
|
5
|
|
|
5
|
1
|
187493
|
my $type = shift; |
|
69
|
5
|
100
|
|
|
|
15
|
my $value = (scalar(@_)>1 ? [@_] : shift); # ref thyself |
|
70
|
|
|
|
|
|
|
# re-bless byte PDLs as PDL::Char |
|
71
|
5
|
100
|
|
|
|
22
|
if (ref($value) =~ /PDL/) { |
|
72
|
1
|
50
|
|
|
|
7
|
PDL::Core::barf('Cannot convert a non-byte PDL to PDL::Char') |
|
73
|
|
|
|
|
|
|
if ($value->get_datatype != $PDL::Types::PDL_B); |
|
74
|
1
|
|
|
|
|
3
|
return bless $value, $type; |
|
75
|
|
|
|
|
|
|
} |
|
76
|
4
|
|
|
|
|
6
|
my $ptype = $PDL::Types::PDL_B; |
|
77
|
4
|
|
|
|
|
28
|
my $self = PDL->initialize(); |
|
78
|
4
|
|
|
|
|
19
|
$self->set_datatype($ptype); |
|
79
|
4
|
50
|
|
|
|
11
|
$value = 0 if !defined($value); |
|
80
|
4
|
|
|
|
|
5
|
my $maxlength; # max length seen for all character strings |
|
81
|
4
|
|
|
|
|
7
|
my $samelen = 1; # Flag = 1 if all character strings are the same length |
|
82
|
|
|
|
|
|
|
# 1st Pass thru the perl array structure, assume all strings the same length |
|
83
|
4
|
|
|
|
|
6
|
my @dims; |
|
84
|
4
|
|
|
|
|
12
|
my $str = _rcharpack($value,\$maxlength,\$samelen,0,\@dims); |
|
85
|
4
|
100
|
|
|
|
11
|
unless( $samelen){ # Strings weren't the same length, go thru again and null pad to |
|
86
|
|
|
|
|
|
|
# the max length. |
|
87
|
1
|
|
|
|
|
6
|
$str = _rcharpack2($value,$maxlength,0,\@dims); |
|
88
|
|
|
|
|
|
|
} |
|
89
|
4
|
|
|
|
|
30
|
$self->setdims([reverse @dims]); |
|
90
|
4
|
|
|
|
|
36
|
$self->update_data_from($str); |
|
91
|
4
|
|
|
|
|
15
|
return bless $self, $type; |
|
92
|
|
|
|
|
|
|
} |
|
93
|
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
# Take an N-D perl array of strings and pack it into a single string, |
|
95
|
|
|
|
|
|
|
# Used by the 'char' constructor |
|
96
|
|
|
|
|
|
|
# |
|
97
|
|
|
|
|
|
|
# References supplied so $maxlength and $samelen are updated along the way as well. |
|
98
|
|
|
|
|
|
|
# |
|
99
|
|
|
|
|
|
|
# |
|
100
|
|
|
|
|
|
|
# This version (_rcharpack) is for the 1st pass thru the N-d string array. |
|
101
|
|
|
|
|
|
|
# It assumes that all strings are the same length, but also checks to see if they aren't |
|
102
|
|
|
|
|
|
|
sub _rcharpack { |
|
103
|
44
|
|
|
44
|
|
45
|
my $w = shift; # Input string |
|
104
|
44
|
|
|
|
|
58
|
my ($maxlenref, $samelenref, $level, $dims) = @_; # reference to $maxlength, $samelen |
|
105
|
44
|
|
|
|
|
41
|
my ($ret,$type); |
|
106
|
44
|
|
|
|
|
36
|
$ret = ""; |
|
107
|
44
|
100
|
|
|
|
103
|
if (ref($w) eq "ARRAY") { |
|
|
|
50
|
|
|
|
|
|
|
108
|
13
|
50
|
66
|
|
|
31
|
PDL::Core::barf('Array is not rectangular') if (defined($dims->[$level]) and |
|
109
|
|
|
|
|
|
|
$dims->[$level] != scalar(@$w)); |
|
110
|
13
|
|
|
|
|
13
|
$dims->[$level] = scalar (@$w); |
|
111
|
13
|
|
|
|
|
12
|
$level++; |
|
112
|
13
|
|
|
|
|
15
|
$type = ref($$w[0]); |
|
113
|
13
|
|
|
|
|
18
|
for(@$w) { |
|
114
|
40
|
50
|
|
|
|
53
|
PDL::Core::barf('Array is not rectangular') unless $type eq ref($_); # Equal types |
|
115
|
40
|
|
|
|
|
52
|
$ret .= _rcharpack($_,$maxlenref, $samelenref, $level, $dims); |
|
116
|
|
|
|
|
|
|
} |
|
117
|
|
|
|
|
|
|
}elsif (ref(\$w) eq "SCALAR") { |
|
118
|
31
|
|
|
|
|
34
|
my $len = length($w); |
|
119
|
|
|
|
|
|
|
# Check for this length being different then the others: |
|
120
|
31
|
100
|
100
|
|
|
70
|
$$samelenref = 0 if( defined($$maxlenref) && ($len != $$maxlenref) ); |
|
121
|
|
|
|
|
|
|
# Save the max length: |
|
122
|
31
|
100
|
66
|
|
|
85
|
$$maxlenref = $len if( !defined($$maxlenref) || $len > $$maxlenref); # see if this is the max length seen so far |
|
123
|
31
|
|
|
|
|
33
|
$dims->[$level] = $len; |
|
124
|
31
|
|
|
|
|
32
|
$ret = $w; |
|
125
|
|
|
|
|
|
|
}else{ |
|
126
|
0
|
|
|
|
|
0
|
PDL::Core::barf("Don't know how to make a PDL object from passed argument"); |
|
127
|
|
|
|
|
|
|
} |
|
128
|
44
|
|
|
|
|
79
|
return $ret; |
|
129
|
|
|
|
|
|
|
} |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
# This version (_rcharpack2) is for the 2nd pass (if required) thru the N-d string array. |
|
132
|
|
|
|
|
|
|
# If the 1st pass thru (_rcharpack) finds that all strings were not the same length, |
|
133
|
|
|
|
|
|
|
# this routine will go thru and null-pad all strings to the max length seen. |
|
134
|
|
|
|
|
|
|
# Note: For efficiency, the error checking is not repeated here, because any errors will |
|
135
|
|
|
|
|
|
|
# already be detected in the 1st pass. |
|
136
|
|
|
|
|
|
|
# |
|
137
|
|
|
|
|
|
|
sub _rcharpack2 { |
|
138
|
9
|
|
|
9
|
|
12
|
my $w = shift; # Input string |
|
139
|
9
|
|
|
|
|
12
|
my ($maxlen, $level, $dims) = @_; # Length to pad strings to |
|
140
|
9
|
|
|
|
|
8
|
my ($ret,$type); |
|
141
|
9
|
|
|
|
|
11
|
$ret = ""; |
|
142
|
9
|
100
|
|
|
|
21
|
if (ref($w) eq "ARRAY") { |
|
|
|
50
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
# Checks not needed the second time thru (removed) |
|
144
|
3
|
|
|
|
|
7
|
$dims->[$level] = scalar (@$w); |
|
145
|
3
|
|
|
|
|
5
|
$level++; |
|
146
|
3
|
|
|
|
|
5
|
$type = ref($$w[0]); |
|
147
|
3
|
|
|
|
|
7
|
for(@$w) { |
|
148
|
8
|
|
|
|
|
16
|
$ret .= _rcharpack2($_,$maxlen,$level,$dims); |
|
149
|
|
|
|
|
|
|
} |
|
150
|
|
|
|
|
|
|
}elsif (ref(\$w) eq "SCALAR") { |
|
151
|
6
|
|
|
|
|
9
|
my $len = length($w); |
|
152
|
6
|
|
|
|
|
9
|
$dims->[$level] = $maxlen; |
|
153
|
6
|
|
|
|
|
22
|
$ret = $w.("\00" x ($maxlen - $len)); |
|
154
|
|
|
|
|
|
|
} |
|
155
|
9
|
|
|
|
|
16
|
return $ret; |
|
156
|
|
|
|
|
|
|
} |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
=head2 string |
|
159
|
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
=for ref |
|
161
|
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
Function to print a character PDL (created by 'char') in a pretty format. |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
=for usage |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
$char = PDL::Char->new( [['abc', 'def', 'ghi'], ['jkl', 'mno', 'pqr']] ); |
|
167
|
|
|
|
|
|
|
print $char; # 'string' bound to "", perl stringify function |
|
168
|
|
|
|
|
|
|
# Prints: |
|
169
|
|
|
|
|
|
|
# [ |
|
170
|
|
|
|
|
|
|
# ['abc' 'def' 'ghi'] |
|
171
|
|
|
|
|
|
|
# ['jkl' 'mno' 'pqr'] |
|
172
|
|
|
|
|
|
|
# ] |
|
173
|
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
# 'string' is overloaded to the "" operator, so: |
|
175
|
|
|
|
|
|
|
# print $char; |
|
176
|
|
|
|
|
|
|
# should have the same effect. |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
=cut |
|
179
|
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
sub string { |
|
181
|
38
|
|
|
38
|
1
|
98
|
my $self = shift; |
|
182
|
38
|
|
100
|
|
|
59
|
my $level = shift || 0; |
|
183
|
38
|
50
|
|
|
|
51
|
my $sep = $PDL::use_commas ? "," : " "; |
|
184
|
38
|
100
|
|
|
|
116
|
if ($self->dims == 1) { |
|
185
|
25
|
|
|
|
|
23
|
my $str = ${$self->get_dataref}; # get copy of string |
|
|
25
|
|
|
|
|
166
|
|
|
186
|
25
|
|
|
|
|
47
|
$str =~ s/\00+$//g; # get rid of any null padding |
|
187
|
25
|
|
|
|
|
158
|
return "\'". $str. "\'". $sep; |
|
188
|
|
|
|
|
|
|
} else { |
|
189
|
13
|
|
|
|
|
25
|
my @dims = reverse $self->dims; |
|
190
|
13
|
|
|
|
|
16
|
my $ret = ''; |
|
191
|
13
|
100
|
|
|
|
29
|
$ret .= (" " x $level) . '[' . ((@dims == 2) ? ' ' : "\n"); |
|
192
|
13
|
|
|
|
|
26
|
for (my $i=0;$i<$dims[0];$i++) { |
|
193
|
33
|
|
|
|
|
47
|
my $slicestr = ":," x (scalar(@dims)-1) . "($i)"; |
|
194
|
33
|
|
|
|
|
111
|
my $substr = $self->slice($slicestr); |
|
195
|
33
|
|
|
|
|
56
|
$ret .= $substr->string($level+1); |
|
196
|
|
|
|
|
|
|
} |
|
197
|
13
|
|
|
|
|
19
|
$ret .= (" " x $level) . ']' . $sep . "\n"; |
|
198
|
13
|
|
|
|
|
59
|
return $ret; |
|
199
|
|
|
|
|
|
|
} |
|
200
|
|
|
|
|
|
|
} |
|
201
|
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
=head2 setstr |
|
203
|
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
=for ref |
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
Function to set one string value in a character PDL. The input position is |
|
207
|
|
|
|
|
|
|
the position of the string, not a character in the string. The first dimension |
|
208
|
|
|
|
|
|
|
is assumed to be the length of the string. |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
The input string will be null-padded if the string is shorter than the first |
|
211
|
|
|
|
|
|
|
dimension of the PDL. It will be truncated if it is longer. |
|
212
|
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
=for usage |
|
214
|
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
$char = PDL::Char->new( [['abc', 'def', 'ghi'], ['jkl', 'mno', 'pqr']] ); |
|
216
|
|
|
|
|
|
|
$char->setstr(0,1, 'foobar'); |
|
217
|
|
|
|
|
|
|
print $char; # 'string' bound to "", perl stringify function |
|
218
|
|
|
|
|
|
|
# Prints: |
|
219
|
|
|
|
|
|
|
# [ |
|
220
|
|
|
|
|
|
|
# ['abc' 'def' 'ghi'] |
|
221
|
|
|
|
|
|
|
# ['foo' 'mno' 'pqr'] |
|
222
|
|
|
|
|
|
|
# ] |
|
223
|
|
|
|
|
|
|
$char->setstr(2,1, 'f'); |
|
224
|
|
|
|
|
|
|
print $char; # 'string' bound to "", perl stringify function |
|
225
|
|
|
|
|
|
|
# Prints: |
|
226
|
|
|
|
|
|
|
# [ |
|
227
|
|
|
|
|
|
|
# ['abc' 'def' 'ghi'] |
|
228
|
|
|
|
|
|
|
# ['foo' 'mno' 'f'] -> note that this 'f' is stored "f\0\0" |
|
229
|
|
|
|
|
|
|
# ] |
|
230
|
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
=cut |
|
232
|
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
sub setstr { # Sets a particular single value to a string. |
|
234
|
3
|
50
|
|
3
|
1
|
990
|
PDL::Core::barf('Usage: setstr($pdl, $x, $y,.., $value)') if $#_<2; |
|
235
|
3
|
|
|
|
|
5
|
my $self = shift; |
|
236
|
3
|
|
|
|
|
6
|
my $val = pop; |
|
237
|
3
|
|
|
|
|
15
|
my @dims = $self->dims; |
|
238
|
3
|
|
|
|
|
4
|
my $n = $dims[0]; |
|
239
|
3
|
|
|
|
|
8
|
for (my $i=0;$i<$n;$i++) { |
|
240
|
9
|
100
|
|
|
|
35
|
my $chr = ($i >= length($val)) ? 0 : unpack ("C", substr ($val, $i, 1)); |
|
241
|
9
|
|
|
|
|
122
|
PDL::Core::set_c ($self, [$i, @_], $chr); |
|
242
|
|
|
|
|
|
|
} |
|
243
|
|
|
|
|
|
|
} |
|
244
|
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
=head2 atstr |
|
246
|
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
=for ref |
|
248
|
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
Function to fetch one string value from a PDL::Char type PDL, given a position within the PDL. |
|
250
|
|
|
|
|
|
|
The input position of the string, not a character in the string. The length of the input |
|
251
|
|
|
|
|
|
|
string is the implied first dimension. |
|
252
|
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
=for usage |
|
254
|
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
$char = PDL::Char->new( [['abc', 'def', 'ghi'], ['jkl', 'mno', 'pqr']] ); |
|
256
|
|
|
|
|
|
|
print $char->atstr(0,1); |
|
257
|
|
|
|
|
|
|
# Prints: |
|
258
|
|
|
|
|
|
|
# jkl |
|
259
|
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
=cut |
|
261
|
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
sub atstr { # Fetchs a string value from a PDL::Char |
|
263
|
3
|
50
|
|
3
|
1
|
13
|
PDL::Core::barf('Usage: atstr($pdl, $x, $y,..,)') if (@_ < 2); |
|
264
|
3
|
|
|
|
|
5
|
my $self = shift; |
|
265
|
3
|
|
|
|
|
7
|
my $str = ':,' . join (',', map {"($_)"} @_); |
|
|
9
|
|
|
|
|
27
|
|
|
266
|
3
|
|
|
|
|
14
|
my $w = $self->slice($str); |
|
267
|
3
|
|
|
|
|
6
|
my $val = ${$w->get_dataref}; # get the data |
|
|
3
|
|
|
|
|
26
|
|
|
268
|
3
|
|
|
|
|
12
|
$val =~ s/\00+$//g; # get rid of any null padding |
|
269
|
3
|
|
|
|
|
29
|
return $val; |
|
270
|
|
|
|
|
|
|
} |
|
271
|
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
# yuck ;) this is a cool little accessor method |
|
273
|
|
|
|
|
|
|
# rebless a slice into PDL; originally |
|
274
|
|
|
|
|
|
|
# Marc's idea used in PDL::Complex |
|
275
|
|
|
|
|
|
|
sub numeric { |
|
276
|
0
|
|
|
0
|
0
|
|
my ($seq) = @_; |
|
277
|
0
|
|
|
|
|
|
return bless $seq->slice(''), 'PDL'; |
|
278
|
|
|
|
|
|
|
} |
|
279
|
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
1; |