forked from szabgab/perlmaven.com
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathopen-and-read-from-files.tt
196 lines (146 loc) · 5.12 KB
/
open-and-read-from-files.tt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
=title Open and read from text files
=timestamp 2013-01-06T10:45:56
=indexes open, <$fh>, read, <, encoding, UTF-8, die, open or die
=status show
=books beginner_book
=author szabgab
=index 1
=archive 1
=feed 1
=comments 1
=social 1
=abstract start
In this part of the <a href="/perl-tutorial">Perl tutorial</a> we are going to see <b>how to read from a file in Perl</b>.
At this time, we are focusing on text files.
=abstract end
There are two common ways to open a file depending on how would you like
to handle error cases.
<h2>Exception</h2>
Case 1: Throw an exception if you cannot open the file:
<code lang="perl">
use strict;
use warnings;
my $filename = 'data.txt';
open(my $fh, '<:encoding(UTF-8)', $filename)
or die "Could not open file '$filename' $!";
while (my $row = <$fh>) {
chomp $row;
print "$row\n";
}
</code>
<h2>Warn or keep silent</h2>
Case 2: Give a warning if you cannot open the file, but keep running:
<code lang="perl">
use strict;
use warnings;
my $filename = 'data.txt';
if (open(my $fh, '<:encoding(UTF-8)', $filename)) {
while (my $row = <$fh>) {
chomp $row;
print "$row\n";
}
} else {
warn "Could not open file '$filename' $!";
}
</code>
<h2>Explanation</h2>
Let's see them explained:
First, using a text editor, create a file called 'data.txt' and add a few lines to it:
<code>
First row
Second row
Third row
</code>
Opening the file for reading is quite similar to how we
<a href="/writing-to-files-with-perl">opened it for writing</a>,
but instead of the "greater-than" (<hl>></hl>) sign, we are using
the "less-than" (<hl><</hl>) sign.
This time we also set the encoding to be UTF-8. In most of the code out there
you will see only the "less-than" sign.
<code lang="perl">
use strict;
use warnings;
my $filename = 'data.txt';
open(my $fh, '<:encoding(UTF-8)', $filename)
or die "Could not open file '$filename' $!";
my $row = <$fh>;
print "$row\n";
print "done\n";
</code>
Once we have the filehandle we can read from it using the same
readline operator that was used for
<a href="/installing-perl-and-getting-started">reading from the keyboard (STDIN)</a>.
This will read the first line of the file.
Then we print out the content of $row and print "done" just
to make it clear we reached the end of our example.
If you run the above script you will see it prints
<code>
First row
done
</code>
Why is there an empty row before the "done" you might ask.
That's because the readline operator read all the line,
including the trailing newline. When we used <hl>print()</hl> to print it out,
we added a second newline.
As with the case of reading from STDIN, here too, we usually don't
need that trailing newline so we will use <hl>chomp()</hl> to remove it.
<h2>Reading more than one line</h2>
Once we know how to read one line we can go ahead and put
the readline call in the condition of a <hl>while</hl> loop.
<code lang="perl">
use strict;
use warnings;
my $filename = $0;
open(my $fh, '<:encoding(UTF-8)', $filename)
or die "Could not open file '$filename' $!";
while (my $row = <$fh>) {
chomp $row;
print "$row\n";
}
print "done\n";
</code>
Every time we reach the condition of the <hl>while</hl> loop, first it will
execute <hl>my $row = <$fh></hl>, the part that will read the next line from the file.
If that line has anything in it, that will evaluate to true.
Even empty lines have a newline at the end, so when we read them, the
<hl>$row</hl> variable will contain a <hl>\n</hl> that will evaluate to true in
boolean context.
After we read the last line, in the next iteration the readline operator (<hl><$fh></hl>) will
return undef which is false. The while-loop will terminate.
<h3>An edge-case</h3>
There is an edge-case though when the very last line has a single 0 in it, without a trailing newline.
The above code would evaluate that line to false and the loop would not be executed. Fortunately,
Perl is actually cheating here. In this very specific case (reading a line from a file within a while-loop),
perl will actually act as if you wrote <hl>while (defined my $row = <$fh>) {</hl> and so even such lines
will execute properly.
<h2>open without die</h2>
The above way of handling files is used in Perl scripts when you absolutely
have to have the file opened or there is no point in running your code.
For example when the whole job of your script is to parse that file.
What if this is an optional configuration file? If you can read it
you change some settings, if you cannot read you just use the defaults.
In that case the second solution might be a better way to
write your code.
<code lang="perl">
if (open(my $fh, '<:encoding(UTF-8)', $filename)) {
while (my $row = <$fh>) {
chomp $row;
print "$row\n";
}
} else {
warn "Could not open file '$filename' $!";
}
</code>
In this case we check the return value of <hl>open</hl>.
If it is true we go ahead and read the content of the file.
If it failed we give a warning using the built-in <hl>warn</hl>
function but don't throw an exception. We don't even need to
include the <hl>else</hl> part:
<code lang="perl">
if (open(my $fh, '<:encoding(UTF-8)', $filename)) {
while (my $row = <$fh>) {
chomp $row;
print "$row\n";
}
}
</code>