Fixed length records: Difference between revisions
SqrtNegInf (talk | contribs) m →{{header|Perl 6}}: fix file open error handling |
SqrtNegInf (talk | contribs) Added Perl example |
||
Line 564:
</pre>
=={{header|Perl}}==
<lang perl>open $in, '<', 'flr-infile.dat';
open $out, '>', 'flr-outfile.dat';
while ($n=sysread($in, $record, 80)) { # read in fixed sized binary chunks
syswrite $out, reverse $record; # write reversed records to file
print reverse($record)."\n" # display reversed records, line-by-line
}
close $out;</lang>
{{out}}
<pre>8.........7.........6.........5.........4.........3.........2.........1...1 eniL
2 eniL
3 eniL
4 eniL
6 eniL
7 eniL
............................................................8 enil detnednI
NIGRAM TR 9 eniL</pre>
[https://github.com/SqrtNegInf/Rosettacode-Perl5-Smoke/blob/master/ref/flr-infile.dat input file]
and
[https://github.com/SqrtNegInf/Rosettacode-Perl5-Smoke/blob/master/ref/flr-outfile.dat output file]
=={{header|Perl 6}}==
Link to a copy of the input file used: [https://github.com/thundergnat/rc/blob/master/resouces/flr-infile.dat flr-infile.dat]
|
Revision as of 14:15, 21 November 2018
![Task](http://static.miraheze.org/rosettacodewiki/thumb/b/ba/Rcode-button-task-crushed.png/64px-Rcode-button-task-crushed.png)
You are encouraged to solve this task according to the task description, using any language you may know.
Fixed length read/write
Before terminals, computers commonly used punch card readers or paper tape input.
A common format before these devices were superseded by terminal technology was based on the Hollerith code, Hollerith code.
These input devices handled 80 columns per card and had a limited character set, encoded by punching holes in one or more rows of the card for each column.
These devices assumed/demanded a fixed line width of 80 characters, newlines were not required (and could not even be encoded in some systems).
- Task
Write a program to read 80 column fixed length records (no newline terminators (but newline characters allowed in the data)) and then write out the reverse of each line as fixed length 80 column records.
Samples here use printable characters, but that is not a given with fixed length data. Filenames used are sample.txt, infile.dat, outfile.dat.
Note: There are no newlines, inputs and outputs are fixed at 80 columns, no more, no less, space padded. Fixed length data is 8 bit complete. NUL bytes of zero are allowed.
These fixed length formats are still in wide use on mainframes, with JCL and with COBOL (which commonly use EBCDIC encoding and not ASCII). Most of the large players in day to day financial transactions know all about fixed length records and the expression logical record length.
- Sample data
To create the sample input file, use an editor that supports fixed length records or use a conversion utility. For instance, most GNU/Linux versions of dd support blocking and unblocking records with a conversion byte size.
Line 1...1.........2.........3.........4.........5.........6.........7.........8 Line 2 Line 3 Line 4 Line 6 Line 7 Indented line 8............................................................ Line 9 RT MARGIN
prompt$ dd if=sample.txt of=infile.dat cbs=80 conv=block
will create a fixed length record file of 80 bytes given newline delimited text input.
prompt$ dd if=infile.dat cbs=80 conv=unblock
will display a file with 80 byte logical record lengths to standard out as standard text with newlines.
- Bonus round
Forth systems often include BLOCK words. A block is 1024 bytes. Source code is stored as 16 lines of 64 characters each (again, no newline character or sequence to mark the end of a line).
Write a program to convert a block file to text (using newlines). Trailing spaces should be excluded from the output.
Also demonstrate how to convert from a normal text file to block form. All lines either truncated or padded to 64 characters with no newline terminators. The last block filled to be exactly 1024 characters by adding blanks if needed. Assume a full range of 8 bit byte values for each character.
The COBOL example uses forth.txt and forth.blk filenames.
COBOL
<lang cobol> *> Rosetta Code, fixed length records
*> Tectonics: *> cobc -xj lrecl80.cob identification division. program-id. lrecl80.
environment division. configuration section. repository. function all intrinsic.
input-output section. file-control. select infile assign to infile-name organization is sequential file status is infile-status . select outfile assign to outfile-name organization is sequential file status is outfile-status .
data division. file section. fd infile. 01 input-text pic x(80).
fd outfile. 01 output-text pic x(80).
working-storage section. 01 infile-name. 05 value "infile.dat". 01 infile-status pic xx. 88 ok-input value '00'. 88 eof-input value '10'.
01 outfile-name. 05 value "outfile.dat". 01 outfile-status pic xx. 88 ok-output value '00'.
procedure division.
open input infile if not ok-input then display "error opening input " infile-name upon syserr goback end-if
open output outfile if not ok-output display "error opening output " outfile-name upon syserr goback end-if
*> read lrecl 80 and write the reverse as lrecl 80 read infile perform until not ok-input move function reverse(input-text) to output-text
write output-text if not ok-output then display "error writing: " output-text upon syserr end-if read infile end-perform
close infile outfile
*> from fixed length to normal text, outfile is now the input file open input outfile if not ok-output then display "error opening input " outfile-name upon syserr goback end-if
read outfile perform until not ok-output display function trim(output-text trailing) read outfile end-perform
close outfile
goback. end program lrecl80.</lang>
Given a starting file sample.txt of
Line 1...1.........2.........3.........4.........5.........6.........7.........8 Line 2 Line 3 Line 4 Line 6 Line 7 Indented line 8............................................................ Line 9 RT MARGIN
And a setup pass of
prompt$ dd if=sample.txt of=infile.dat cbs=80 conv=block
- Output:
prompt$ cobc -xj fixed-length.cob 8.........7.........6.........5.........4.........3.........2.........1...1 eniL 2 eniL 3 eniL 4 eniL 6 eniL 7 eniL ..............................................................8 enil fo tuO NIGRAM TR 9 eniL
prompt$ file outfile.dat outfile.dat: ASCII text, with very long lines, with no line terminators
blocks
Demonstrate text to Forth source block form.
<lang cobol> *> Rosetta Code fixed length records, text to Forth block
identification division. program-id. blocking.
environment division. configuration section. repository. function all intrinsic.
input-output section. file-control. select infile assign to infile-name organization is line sequential file status is infile-status . select outfile assign to outfile-name organization is sequential file status is outfile-status .
data division. file section. fd infile. 01 input-text pic x(64).
fd outfile. 01 output-text pic x(64).
working-storage section. 01 infile-name. 05 value "forth.txt". 01 infile-status pic xx. 88 ok-input value '00'. 88 eof-input value '10'.
01 outfile-name. 05 value "forth.blk". 01 outfile-status pic xx. 88 ok-output value '00'.
procedure division.
*> read a line, padded to or truncated at 64 as defined in FD open input infile if not ok-input then display "error opening input " infile-name upon syserr goback end-if
open output outfile if not ok-output display "error opening output " outfile-name upon syserr goback end-if
move 0 to tally read infile perform until not ok-input move input-text to output-text
write output-text if not ok-output then display "error writing: " output-text upon syserr end-if
add 1 to tally if tally > 15 then move 0 to tally end-if
read infile end-perform
*> Output up to next 1024 byte boundary if tally > 0 then compute tally = 16 - tally move spaces to output-text
perform tally times write output-text if not ok-output then display "error writing: " output-text upon syserr end-if end-perform end-if
close infile outfile
goback. end program blocking.</lang>
Demonstrate Forth source block to text form.
<lang cobol> *> Rosetta Code fixed length records, Forth blocks to text.
identification division. program-id. unblocking.
environment division. configuration section. repository. function all intrinsic.
input-output section. file-control. select infile assign to infile-name organization is sequential file status is infile-status . select outfile assign to outfile-name organization is line sequential file status is outfile-status .
data division. file section. fd infile. 01 input-text pic x(64).
fd outfile. 01 output-text pic x(64).
working-storage section. 01 infile-name. 05 value "forth.blk". 01 infile-status pic xx. 88 ok-input value '00'. 88 eof-input value '10'.
01 outfile-name. 05 value "forth.txt". 01 outfile-status pic xx. 88 ok-output value '00'.
procedure division.
open input infile if not ok-input then display "error opening input " trim(infile-name) upon syserr goback end-if
open output outfile if not ok-output display "error opening write " trim(outfile-name) upon syserr goback end-if
*> read a fixed length line, 64 characters read infile perform until not ok-input move trim(input-text) to output-text
write output-text if not ok-output then display "error writing: " output-text upon syserr end-if read infile end-perform
close infile outfile
goback. end program unblocking.</lang>
Julia
The program reads from an "infile.dat" file created with dd, as described in the task instructions. <lang julia> function processfixedlengthrecords(infname, blocksize, outfname)
inf = open(infname) outf = open(outfname, "w") filedata = [ read(inf, blocksize) for _ in 1:10 ] for line in filedata s = join([Char(c) for c in line], "") @assert(length(s) == blocksize) write(outf, s) end
end
processfixedlengthrecords("infile.dat", 80, "outfile.dat") </lang>
Go
<lang go>package main
import (
"fmt" "log" "os" "os/exec"
)
func reverseBytes(bytes []byte) {
for i, j := 0, len(bytes)-1; i < j; i, j = i+1, j-1 { bytes[i], bytes[j] = bytes[j], bytes[i] }
}
func check(err error) {
if err != nil { log.Fatal(err) }
}
func main() {
in, err := os.Open("infile.dat") check(err) defer in.Close()
out, err := os.Create("outfile.dat") check(err)
record := make([]byte, 80) empty := make([]byte, 80) for { n, err := in.Read(record) if err != nil { if n == 0 { break // EOF reached } else { out.Close() log.Fatal(err) } } reverseBytes(record) out.Write(record) copy(record, empty) } out.Close()
// Run dd from within program to write output.dat // to standard output as normal text with newlines. cmd := exec.Command("dd", "if=outfile.dat", "cbs=80", "conv=unblock") bytes, err := cmd.Output() check(err) fmt.Println(string(bytes))
}</lang>
- Output:
8.........7.........6.........5.........4.........3.........2.........1...1 eniL 2 eniL 3 eniL 4 eniL 6 eniL 7 eniL ............................................................8 enil detnednI NIGRAM TR 9 eniL
Bonus round
<lang go>package main
import (
"bufio" "fmt" "log" "os" "strings"
)
func check(err error) {
if err != nil { log.Fatal(err) }
}
func block2text(inputFile, outputFile string) {
in, err := os.Open(inputFile) check(err) defer in.Close()
out, err := os.Create(outputFile) check(err) defer out.Close()
line := make([]byte, 64) empty := make([]byte, 64) for { n, err := in.Read(line) if err != nil { if n == 0 { break // EOF reached } else { log.Fatal(err) } } str := string(line) str = strings.TrimRight(str, " \000") out.WriteString(str + "\n") copy(line, empty) }
}
func text2block(inputFile, outputFile string) {
in, err := os.Open(inputFile) check(err) defer in.Close()
out, err := os.Create(outputFile) check(err) defer out.Close()
scanner := bufio.NewScanner(in) count := 0 for scanner.Scan() { str := scanner.Text() count++ le := len(str) if le > 64 { str = str[0:64] } else if le < 64 { str = fmt.Sprintf("%-64s", str) } out.WriteString(str) } if rem := count % 16; rem > 0 { str := strings.Repeat(" ", (16-rem)*64) out.WriteString(str) }
}
func main() {
block2text("block.dat", "block.txt") text2block("block.txt", "block2.dat")
}</lang>
Neko
<lang ActionScript>/**
fixed length records, in Neko
- /
var LRECL = 80
var reverse = function(s) {
var len = $ssize(s) if len < 2 return s
var reverse = $smake(len) var pos = 0 while len > 0 $sset(reverse, pos ++= 1, $sget(s, len -= 1)) return reverse
}
var file_open = $loader.loadprim("std@file_open", 2) var file_read = $loader.loadprim("std@file_read", 4) var file_write = $loader.loadprim("std@file_write", 4) var file_close = $loader.loadprim("std@file_close", 1)
var input = file_open("infile.dat", "r") var output = file_open("outfile.dat", "w")
var len var pos = 0 var record = $smake(LRECL)
while true {
try { len = file_read(input, record, pos, LRECL) if len != LRECL $throw("Invalid read")
len = file_write(output, reverse(record), pos, LRECL) if len != LRECL $throw("Invalid write") } catch a break;
} file_close(input) file_close(output)</lang>
- Output:
prompt$ dd if=infile.txt cbs=80 conv=block status=none of=infile.dat prompt$ nekoc fixed-length.neko prompt$ neko fixed-length prompt$ dd if=outfile.dat cbs=80 conv=unblock status=none 8.........7.........6.........5.........4.........3.........2.........1...1 eniL 2 eniL 3 eniL 4 eniL 6 eniL 7 eniL ............................................................8 enil detnednI NIGRAM TR 9 eniL
Perl
<lang perl>open $in, '<', 'flr-infile.dat'; open $out, '>', 'flr-outfile.dat';
while ($n=sysread($in, $record, 80)) { # read in fixed sized binary chunks
syswrite $out, reverse $record; # write reversed records to file print reverse($record)."\n" # display reversed records, line-by-line
} close $out;</lang>
- Output:
8.........7.........6.........5.........4.........3.........2.........1...1 eniL 2 eniL 3 eniL 4 eniL 6 eniL 7 eniL ............................................................8 enil detnednI NIGRAM TR 9 eniL
Perl 6
Link to a copy of the input file used: flr-infile.dat
Essentially the same as task Selective_File_Copy except more boring.
<lang perl6>$*OUT = './flr-outfile.dat'.IO.open(:w, :bin) orelse .die; # open a file in binary mode for writing while my $record = $*IN.read(80) { # read in fixed sized binary chunks
$*OUT.write: $record.=reverse; # write reversed records out to $outfile $*ERR.say: $record.decode('ASCII'); # display decoded records on STDERR
} close $*OUT;</lang>
- Output:
8.........7.........6.........5.........4.........3.........2.........1...1 eniL 2 eniL 3 eniL 4 eniL 6 eniL 7 eniL ............................................................8 enil detnednI NIGRAM TR 9 eniL
REXX
<lang rexx>/*REXX pgm reads fixed-length 80 byte records; reverses each record, displays to term. */ iFID= 'FIXEDLEN.TXT' /*the file's filename (used for input).*/ call charin iFID, 1, 0 /*open the file, point rec pointer to 1*/
/* [+] just to be safe, position file.*/ do j=1 while chars(iFID)>=80 /*read data records of LRECL ≡ eighty. */ @.j= charin(iFID, , 80) /*read a data record of eighty bytes. */ end /*j*/
- = j - 1 /*adjust # of records (J is 1 too high)*/
do k=1 for # /* [+] process all the records read. */ say reverse(@.k) /* reverse a record and write to term. */ end /*k*/ /*stick a fork in it, we're all done. */</lang>
- output when using the default input file:
- Output:
8.........7.........6.........5.........4.........3.........2.........1...1 eniL 2 eniL 3 eniL 4 eniL 6 eniL 7 eniL ............................................................8 enil detnednI NIGRAM TR 9 eniL
zkl
Reading from the dd formatted file, which is 720 characters:
Line 1...1.........2.........3.........4.........5.........6.........7.........8Line 2 Line 3 Line 4 Line 6 Line 7 Indented line 8............................................................Line 9 RT MARGIN
<lang zkl>File("infile.dat","rb") // could contain nulls and newlines
// Since we are writing to a ASCII terminal, ignore nulls
.walker(3).chunk(80,String).pump(Console.println,"reverse"); // 3-->read chars</lang>
- Output:
8.........7.........6.........5.........4.........3.........2.........1...1 eniL 2 eniL 3 eniL 4 eniL 6 eniL 7 eniL ............................................................8 enil detnednI NIGRAM TR 9 eniL
To write to a file (as one big line), preserving nulls: <lang zkl>in,out := File("infile.dat","rb"), File("outfile.dat","wb"); in.walker(0).chunk(80).pump(out,"reverse"); // may contain nulls and newlines // 0-->read bytes, chunk to list of bytes, reverse and write the bytes in.close(); out.close();</lang> outfile.dat:
- Output:
8.........7.........6.........5.........4.........3.........2.........1...1 eniL 2 eniL 3 eniL 4 eniL 6 eniL 7 eniL............................................................8 enil detnednI NIGRAM TR 9 eniL
Bonus Round <lang zkl>// read block file (as in read a file of blocks) to text fcn readFourthBlock(inFileNm){
out,f,buf := Sink(String), File(inFileNm,"rb"), Data(1024); while(f.read(1024,buf,False)){ // read 64 chars from buf, strip ws from right side, repeat buf.walker(3).chunk(64,String).pump(out,T("strip",1),'+("\n")); } f.close(); out.close();
}</lang> <lang zkl>// read text file and write as block to file fcn formatToFourthBlock(inFileNm,outFileNm){
n,blk,in,out := 0, Data(), File(inFileNm,"r"), File(outFileNm,"wb"); foreach line in (in.walker(11)){ // right side stripped if(not line) blk.write(" "*64); else blk.write(line.walker().chunk(64,String).pump(String,"%-64s".fmt)); if(blk.len()==1024){ out.write(blk); blk.clear(); } } if(blk) out.write(blk, " "*(1024 - blk.len())); f.close(); out.close();
}</lang>