line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
2
|
|
|
2
|
|
32638
|
use strict; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
73
|
|
2
|
2
|
|
|
2
|
|
12
|
use warnings; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
118
|
|
3
|
|
|
|
|
|
|
package Business::PLZ; |
4
|
|
|
|
|
|
|
{ |
5
|
|
|
|
|
|
|
$Business::PLZ::VERSION = '0.11'; |
6
|
|
|
|
|
|
|
} |
7
|
|
|
|
|
|
|
#ABSTRACT: Validate German postal codes and map them to states |
8
|
|
|
|
|
|
|
|
9
|
2
|
|
|
2
|
|
2138
|
use Tree::Binary::Search 1.0; |
|
2
|
|
|
|
|
13913
|
|
|
2
|
|
|
|
|
100
|
|
10
|
2
|
|
|
2
|
|
1497
|
use overload '""' => sub { ${$_[0]} }; |
|
2
|
|
|
117
|
|
1028
|
|
|
2
|
|
|
|
|
25
|
|
|
117
|
|
|
|
|
241
|
|
|
117
|
|
|
|
|
446
|
|
11
|
2
|
|
|
2
|
|
155
|
use Carp 'croak'; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
926
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
our $STATES; |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
# http://web.archive.org/web/*/http://www.uni-koeln.de/~arcd2/3d.htm |
16
|
|
|
|
|
|
|
BEGIN { |
17
|
2
|
|
|
2
|
|
95
|
my %RANGES = ( |
18
|
|
|
|
|
|
|
BW => [qw(68000-68309 68520-68549 68700-69234 69240-69429 69435-69469 |
19
|
|
|
|
|
|
|
69489-69502 69510-69514 70000-76709 77600-79879 88000-88099 |
20
|
|
|
|
|
|
|
88180-89198 89300-89619 97860-97999)], |
21
|
|
|
|
|
|
|
BY => [qw(63700-63939 80000-87490 87493-87561 87570-87789 88100-88179 |
22
|
|
|
|
|
|
|
89200-89299 90000-96489 97000-97859)], |
23
|
|
|
|
|
|
|
BE => [qw(10000-12527 12531-14199)], |
24
|
|
|
|
|
|
|
BB => [qw(01940-01998 03000-03253 04890-04938 12529 |
25
|
|
|
|
|
|
|
14400-16949 17260-17291 19340-19357)], |
26
|
|
|
|
|
|
|
HB => [qw(27500-27580 28000-28779)], |
27
|
|
|
|
|
|
|
HH => [qw(20000-21149 22000-22769 27499)], |
28
|
|
|
|
|
|
|
HE => [qw(34000-34329 34356-34399 34440-36399 37195 37200-37299 |
29
|
|
|
|
|
|
|
55240-55252 59969 60000-63699 64200-65556 65583-65620 65627 |
30
|
|
|
|
|
|
|
65700-65936 68501-68519 68600-68649 69235-69239 69430-69434 |
31
|
|
|
|
|
|
|
69479-69488 69503-69509 69515-69518)], |
32
|
|
|
|
|
|
|
MV => [qw(17000-17259 17300-19260 19280-19339 19360-19417 23920-23999)], |
33
|
|
|
|
|
|
|
NI => [qw(19270-19273 21202-21449 21522 21600-21789 26000-27478 27607-27809 |
34
|
|
|
|
|
|
|
28784-29399 29430-31868 34330-34355 37000-37194 37197-37199 |
35
|
|
|
|
|
|
|
37400-37649 37689-37691 37697-38479 38500-38729 48442-48465 |
36
|
|
|
|
|
|
|
48478-48480 48486-48488 48497-48531 49000-49459 49550-49849)], |
37
|
|
|
|
|
|
|
NW => [qw(32000-33829 34400-34439 37650-37688 37692-37696 40000-48432 |
38
|
|
|
|
|
|
|
48466-48477 48481-48485 48489-48496 48541-48739 49461-49549 |
39
|
|
|
|
|
|
|
50100-51597 51600-53359 53580-53604 53620-53949 57000-57489 |
40
|
|
|
|
|
|
|
58000-59968)], |
41
|
|
|
|
|
|
|
RP => [qw(51598 53400-53579 53614-53619 54200-55239 55253-56869 57500-57648 |
42
|
|
|
|
|
|
|
65558-65582 65621-65626 65629 66460-66509 66840-67829 76710-76891)], |
43
|
|
|
|
|
|
|
SL => [qw(66000-66459 66510-66839)], |
44
|
|
|
|
|
|
|
SN => [qw(01000-01936 02600-02999 04000-04579 04640-04889 07917-07919 |
45
|
|
|
|
|
|
|
07951-07952 07982-07985 08000-09669)], |
46
|
|
|
|
|
|
|
ST => [qw(06000-06548 06600-06928 29400-29416 38480-38489 38800-39649)], |
47
|
|
|
|
|
|
|
SH => [qw(21450-21521 21524-21529 22801-23919 24000-25999 27483-27498)], |
48
|
|
|
|
|
|
|
TH => [qw(04580-04639 06550-06578 07300-07907 07920-07950 07953-07980 |
49
|
|
|
|
|
|
|
07987-07989 36400-36469 37300-37359 96500-96529 98500-99998)], |
50
|
|
|
|
|
|
|
8 => [qw(87567-87569)], # Kleinwalsertal, Vorarlberg |
51
|
|
|
|
|
|
|
7 => [87491], # Jungholz, Tirol |
52
|
|
|
|
|
|
|
); |
53
|
|
|
|
|
|
|
|
54
|
2
|
|
|
|
|
13
|
$STATES = Tree::Binary::Search->new; |
55
|
|
|
|
|
|
|
$STATES->setComparisonFunction(sub { |
56
|
3238
|
|
|
|
|
83931
|
my ($a1,$a2) = split '-', $_[0]; |
57
|
3238
|
|
|
|
|
6397
|
my ($b1,$b2) = split '-', $_[1]; |
58
|
3238
|
100
|
|
|
|
6378
|
$a2 = $a1 unless defined $a2; |
59
|
3238
|
100
|
|
|
|
5330
|
$b2 = $b1 unless defined $b2; |
60
|
3238
|
100
|
|
|
|
7196
|
return -1 if $a2 < $b1; |
61
|
2721
|
100
|
|
|
|
8535
|
return +1 if $a1 > $b2; |
62
|
12
|
|
|
|
|
32
|
return 0; |
63
|
2
|
|
|
|
|
47
|
}); |
64
|
2
|
|
|
|
|
22
|
while (my ($state,$ranges) = each(%RANGES)) { |
65
|
36
|
|
|
|
|
1127
|
foreach my $plz (@$ranges) { |
66
|
280
|
|
|
|
|
7402
|
$STATES->insert($plz,$state); |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
} |
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
# TODO: see http://anchje.de/inv_rep2.htm for more expections |
72
|
|
|
|
|
|
|
# 21039 SH and HH |
73
|
|
|
|
|
|
|
# 37194 HE and NE |
74
|
|
|
|
|
|
|
# 59969 HE and NW |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
sub new { |
77
|
17
|
|
|
17
|
0
|
3980
|
my ($class, $code) = @_; |
78
|
17
|
|
33
|
|
|
76
|
$class = ref $class || $class; |
79
|
|
|
|
|
|
|
|
80
|
17
|
100
|
66
|
|
|
1026
|
croak 'invalid postal code' unless $code and $code =~ qr/^\d{5}$/; |
81
|
|
|
|
|
|
|
|
82
|
10
|
|
|
|
|
81
|
bless \$code, $class; |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
sub state { |
86
|
9
|
|
|
9
|
1
|
25
|
my $plz = shift; |
87
|
9
|
100
|
66
|
|
|
60
|
$plz = Business::PLZ->new( $plz ) |
88
|
|
|
|
|
|
|
unless ref $plz and $plz->isa('Business::PLZ'); |
89
|
|
|
|
|
|
|
# Tree::Binary throws on exception if key does not exist :-( |
90
|
9
|
100
|
|
|
|
36
|
return $STATES->exists($plz) ? $STATES->select($plz) : undef; |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
sub exists { |
94
|
2
|
|
|
2
|
1
|
295
|
my $state = state(shift); |
95
|
2
|
100
|
|
|
|
42
|
return defined $state ? 1 : 0; |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
sub iso_state { |
99
|
3
|
|
100
|
3
|
1
|
995
|
my $state = state(shift) || return; |
100
|
1
|
50
|
|
|
|
23
|
return ($state =~ /[A-Z][A-Z]/) ? "DE-$state" : "AT-$state"; |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
1; |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
=pod |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
=head1 NAME |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
Business::PLZ - Validate German postal codes and map them to states |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
=head1 VERSION |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
version 0.11 |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=head1 SYNOPSIS |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
use Business::PLZ; |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
my $plz = Business::PLZ->new('12345'); # croaks on invalid code |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
print "$plz"; # stringify |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
$plz->state; # state or undef if not exist |
126
|
|
|
|
|
|
|
$plz->iso_state; # state as full ISO code |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
=head1 DESCRIPTION |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
This module validates German postal codes and maps them to states. |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
=head1 METHODS |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=head2 state |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
Returns the state ("Bundesland") of a postal code as ISO 3166-2 subdivision |
137
|
|
|
|
|
|
|
code. The country prefix 'DE-' (or 'AT-') is not included. Some postal codes |
138
|
|
|
|
|
|
|
belong to more than one state - in this case only one state is returned. A |
139
|
|
|
|
|
|
|
future version of this module may also return multiple states. |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
If no state was found (so the postal code likely does not exists), this |
142
|
|
|
|
|
|
|
method returns undef. The method 'exists' is based on this lookup. |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
To get more information about a state, you can use L: |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
$state_code = $plz->state; |
147
|
|
|
|
|
|
|
$state_name = Locale::SubCountry->new('DE')->full_name( $state_code ); |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=head2 iso_state |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
Returns the state of a postal code as ISO 3166-2 subdivision code, including |
152
|
|
|
|
|
|
|
country prefix. |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
=head2 exists |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
Returns whether the postal code is assigned. This is exactely the case if |
157
|
|
|
|
|
|
|
it can be mapped to a state. |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
=head1 SEE ALSO |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
There are some country-specific modules to handle postal codes, for instance |
162
|
|
|
|
|
|
|
L and L. L contains |
163
|
|
|
|
|
|
|
regular expressions for postal codes of almost every country. |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
=head1 AUTHOR |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
Jakob Voß |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
This software is copyright (c) 2013 by Jakob Voß. |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
This is free software; you can redistribute it and/or modify it under |
174
|
|
|
|
|
|
|
the same terms as the Perl 5 programming language system itself. |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
=cut |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
__END__ |