Selective file copy
Copy part of input records to an output file.
Show how file processing as known from PL/I or COBOL can be implemented in the
language of your choice.
Here, a file is not 'just' a sequence of bytes or lines but a sequence of recods (structured data). The structure is usually described by declarations contained in an INCLUDE file (PL/I) or COPY BOOK (COBOL).
The by name assignment is a little extra available in PL/I.
Data conversions may be necessary (as shown here for data element c.
Go
JSON is popular these days for structured data and Go has support for this kind of selective copy in the JSON reader. The reader also supports custom conversions on fields. The common idiom for record construction and field initialization is shown. <lang go>package main
import (
"encoding/json" "log" "os" "bytes" "errors" "strings"
)
// structure of test file, generated and then used as input. type s1 struct {
A string B string C int D string
}
// structure of output file type s2 struct {
A string C intString // it's a string, but unmarshals from a JSON int. X string
}
type intString string
func (i *intString) UnmarshalJSON(b []byte) error {
if len(b) == 0 || bytes.IndexByte([]byte("0123456789-"), b[0]) < 0 { return errors.New("Unmarshal intString expected JSON number") } *i = intString(b) return nil
}
// "constructor" initializes X func NewS2() *s2 {
return &s2{X: "XXXXX"}
}
func main() {
// generate test file o1, err := os.Create("o1.json") if err != nil { log.Fatal(err) } e := json.NewEncoder(o1) for i := 1; i <= 5; i++ { err := e.Encode(s1{ strings.Repeat("A", i), strings.Repeat("B", i), i, strings.Repeat("D", i), }) if err != nil { log.Fatal(err) } } o1.Close()
// reopen the test file, also open output file in, err := os.Open("o1.json") if err != nil { log.Fatal(err) } out, err := os.Create("out.json") if err != nil { log.Fatal(err) } // copy input to output, streaming d := json.NewDecoder(in) e = json.NewEncoder(out) for d.More() { // a little different than the PL/I example. PL/I reads into s1, then // does the selective copy in memory. The Go JSON reader can read the // s1 formated JSON directly into the s2 Go struct without needing any // intermediate s1 struct. s := NewS2() if err = d.Decode(s); err != nil { log.Fatal(err) } if err = e.Encode(s); err != nil { log.Fatal(err) } }
}</lang>
- o1.json:
{"A":"A","B":"B","C":1,"D":"D"} {"A":"AA","B":"BB","C":2,"D":"DD"} {"A":"AAA","B":"BBB","C":3,"D":"DDD"} {"A":"AAAA","B":"BBBB","C":4,"D":"DDDD"} {"A":"AAAAA","B":"BBBBB","C":5,"D":"DDDDD"}
- out.json:
{"A":"A","C":"1","X":"XXXXX"} {"A":"AA","C":"2","X":"XXXXX"} {"A":"AAA","C":"3","X":"XXXXX"} {"A":"AAAA","C":"4","X":"XXXXX"} {"A":"AAAAA","C":"5","X":"XXXXX"}
Java
With a little help from my friens <lang java>import java.io.BufferedWriter; import java.io.FileWriter; import java.io.File; import java.io.IOException; import java.util.Scanner;
class CopysJ {
public static void main(String[] args) { String ddname_IN = "copys.in.txt"; String ddname_OUT = "copys.out.txt"; if (args.length >= 1) { ddname_IN = args[0].length() > 0 ? args[0] : ddname_IN; } if (args.length >= 2) { ddname_OUT = args[1].length() > 0 ? args[1] : ddname_OUT; }
File dd_IN = new File(ddname_IN); File dd_OUT = new File(ddname_OUT);
try ( Scanner scanner_IN = new Scanner(dd_IN); BufferedWriter writer_OUT = new BufferedWriter(new FileWriter(dd_OUT)) ) { String a; String b; String c; String d; String c1; String x = "XXXXX"; String data_IN; String data_OUT; int ib;
while (scanner_IN.hasNextLine()) { data_IN = scanner_IN.nextLine(); ib = 0; a = data_IN.substring(ib, ib += 5); b = data_IN.substring(ib, ib += 5); c = data_IN.substring(ib, ib += 4); c1=Integer.toHexString(new Byte((c.getBytes())[0]).intValue()); if (c1.length()<2) { c1="0" + c1; } data_OUT = a + c1 + x; writer_OUT.write(data_OUT); writer_OUT.newLine(); System.out.println(data_IN); System.out.println(data_OUT); System.out.println(); } } catch (IOException ex) { ex.printStackTrace(); } return; }
}</lang>
NetRexx
with a little help from a friend <lang netrexx>/* NetRexx */ -- nrc -keepasjava -savelog copys options replace format comments java crossref symbols nobinary
parse arg ddname_IN ddname_OUT . do
if ddname_IN.length = 0 then ddname_IN = 'copys.in.txt' if ddname_OUT.length = 0 then ddname_OUT = 'copys.out.txt'
dd_IN = File(ddname_IN) dd_OUT = File(ddname_OUT) scanner_IN = Scanner(dd_IN) writer_OUT = BufferedWriter(FileWriter(dd_OUT))
x = 'XXXXX' loop while scanner_IN.hasNextLine() data_IN = scanner_IN.nextLine() parse data_IN a +5 . /* b */ +5 c +4 . /* d */ cc=c.left(1).c2x data_OUT = a || cc.right(2,0) || x writer_OUT.write(data_OUT) writer_OUT.newLine() end
catch ex = IOException
ex.printStackTrace()
finally
do if scanner_IN \= null then scanner_IN.close() if writer_OUT \= null then writer_OUT.close() catch ex = IOException ex.printStackTrace() end
end</lang>
ooRexx
<lang oorexx>/* REXX */ infile ="in.txt" outfile="out.txt"
s1=.copys~new(infile,outfile) loop i=1 to 5
s1~~input~~output
end s1~close -- close streams (files) 'type' outfile
- class copys
- attribute a
- attribute b
- attribute c
- attribute d
- method init -- constructor
expose instream outstream parse arg infile, outfile instream =.stream~new(infile)~~open outstream=.stream~new(outfile)~~open("replace")
- method input -- read an input line
expose instream a b c d parse value instream~linein with a +5 b +5 c +5 d +5
- method output -- write an output line
expose outstream a c outstream~lineout(a || c~c2x~left(2)'XXXXX')
- method close -- close files
expose instream outstream instream~close outstream~close</lang>
- Output:
AA 01XXXXX AAA 02XXXXX AAAA 03XXXXX AAAAA04XXXXX AAAAA05XXXXX
PL/I
<lang pli>*process source attributes xref or(!);
copys: Proc Options(Main); Dcl 1 s1 unal, 2 a Char(5), 2 b Char(5), 2 c Bin Fixed(31), 2 d Char(5); Dcl 1 s2, 2 a Char(5), 2 c Pic'99', 2 x Char(5) Init('XXXXX'); Dcl o1 Record Output; /* create a test file */ Dcl in Record Input; Dcl out Record Output; Do i=1 To 5; s1.a=repeat('A',i); s1.b=repeat('B',i); s1.c=i; s1.d=repeat('D',i); Write File(o1) From(s1); End; Close File(o1);
On Endfile(in) Goto eoj; Do i=1 By 1; /* copy parts of the test file */ Read File(in) Into(s1); s2=s1, by name; /* only fields a and c are copied */ Write File(out) From(s2); End; eoj: End;
</lang>
- Output:
AA 01XXXXX AAA 02XXXXX AAAA 03XXXXX AAAAA04XXXXX AAAAA05XXXXX
REXX
<lang rexx>in='in.txt' out='out.txt'; 'erase' out Do While lines(in)>0
l=linein(in) Parse Var l a +5 b +5 c +4 d +5 chex=c2x(c) cpic=left(chex,2) call lineout out,a||cpic||'XXXXX' End
Call lineout in Call lineout out 'type' out</lang>
- Output:
Using the test file produced by PL/I. The data conversion used for c is not very general!
AA 01XXXXX AAA 02XXXXX AAAA 03XXXXX AAAAA04XXXXX AAAAA05XXXXX