aboutsummaryrefslogtreecommitdiff
path: root/challenge-098/jo-37/perl/ch-1.pl
blob: 2af4affc2164bbcae6e4f5ead630ef8d5d52b000 (plain)
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
#!/usr/bin/perl -s

use v5.16;
use Test2::V0;
use warnings FATAL => 'all';
use experimental 'signatures';

our ($tests, $examples);

run_tests() if $tests || $examples;    # does not return

die <<EOS unless @ARGV > 1;
usage: $0 [-examples] [-tests] [file1 c11... fileN cN1...]

-examples
    run examples from the challenge

-tests
    run some tests

fileX (not numeric)
    select file for following read operation(s)

cij (numeric)
    number of characters to read from selected file

Operations on different files may be interleaved.  The file position is
kept separately for each filename.

Example:

ch-1_A.txt: 1234567890
ch-1_B.txt: abcdefghij

./ch-1.pl ch-1_A.txt 4 3 ch-1_B.txt 1 2 ch-1_A.txt 2 1 ch-1_B.txt 3 4

Output:
1234
567
a
bc
89
0
def
ghij

EOS


### Input and Output

binmode STDOUT, ':utf8';

/^\d+$/ and say readN($a, $_) or $a = $_ for @ARGV;


### Implementation

# Read up to $n characters from named file at current position.  Will
# start over from the beginning after eof was detected for the named
# file.
sub readN ($file, $n) {

    # Track filehandles for named files.
    state %fh;

    # Open filehandle for reading characters, not bytes.
    open $fh{$file}, '<:encoding(utf8)', $file or die "$file: $!"
        unless $fh{$file};

    my $nchar = read $fh{$file}, (my $read), $n;
    die "$file: $!" unless defined $nchar;

    # Close filehandle if eof was detected.
    delete $fh{$file} if $nchar < $n;

    $read;
}


### Examples and tests

sub run_tests {
    SKIP: {
        skip 'examples' unless $examples;
        is readN('ch-1_A.txt', 4), '1234', 'example: first chunk';
        is readN('ch-1_A.txt', 4), '5678', 'example: second chunk';
        is readN('ch-1_A.txt', 4), '90', 'example: third chunk';
    }

    SKIP: {
        skip 'tests' unless $tests;
        is readN('ch-1_A.txt', 10), '1234567890', 'read all';
        is readN('ch-1_A.txt', 1), '', 'at eof';
        is readN('ch-1_A.txt', 1), '1', 'file reopened';
        ok readN('ch-1_A.txt', 10), 'seek eof';
        is readN('ch-1_A.txt', 11), '1234567890', 'read all, hit eof';
        is readN('ch-1_A.txt', 1), '1', 'file reopened';
        is readN('ch-1_C.txt', 3), 'Bär', 'multibyte character';
    }

    done_testing;
    exit;
}