We can create streams of data from files, network resources, memory locations, etc, both input and output. To initially demonstrate the use of streams, we'll use streams around a file, both byte and Character streams. The methods introduced in these example can be used for any stream.
InputStreams and OutputStreams are streams of bytes:
def fos= new FileOutputStream('TestFile.txt')
[ 21, 34, 43, 79 ].each{ fos.write(it) }
fos.flush()
fos.write([69, 32, 22] as byte[])
fos.write([10, 11, 12, 13, 88, 89] as byte[], 3, 2)
fos.close()
try{ fos.write(77); assert 0 }catch(e){ assert e instanceof IOException }
assert new File('TestFile.txt').readBytes().toList() ==
[ 21, 34, 43, 79, 69, 32, 22, 13, 88 ]
def fis= new FileInputStream('TestFile.txt')
assert fis.available() == 9
assert fis.read() == 21 fis.skip(2) assert fis.available() == 6
def ba2= new byte[3]
fis.read(ba2)
assert ba2.toList() == [79, 69, 32]
def ba3= new byte[6]
assert fis.read(ba3, 3, 2) == 2 assert ba3.toList() == [0, 0, 0, 22, 13, 0]
assert fis.read(ba3) == 1 assert ba3.toList() == [88, 0, 0, 22, 13, 0]
assert fis.read(ba3) == -1
if( fis.markSupported() ){
fis.reset()
assert fis.read() == 21
fis.mark(0) fis.read(new byte[4])
fis.reset() assert fis.read() == 34
}
fis.close()
try{ fis.read(); assert 0 }catch(e){ assert e instanceof IOException }
new File('TestFile.txt').delete()
Readers and Writers are streams of Characters:
def fw= new FileWriter('TestFile.txt')
[ 'a', 'b' ].each{ fw.write(it as char) } [ 'cd', 'efg' ].each{ fw.write(it) } fw.flush()
fw.write(['h', 'i', 'j'] as char[])
fw.write(['h', 'i', 'j', 'k', 'l', 'm'] as char[], 3, 2)
fw.write('klmnopq', 2, 4) fw.append('q' as char). append('rstuv').
append('uvwxyz', 2, 6)
fw.close()
try{ fw.write('z'); assert 0 }catch(e){ assert e instanceof IOException }
assert new File('TestFile.txt').readLines() == [ 'abcdefghijklmnopqrstuvwxyz' ]
def fr= new FileReader('TestFile.txt')
if(fr.ready()){
assert fr.read() == 'a'
fr.skip(2) def ca2= new char[3]
fr.read(ca2)
assert ca2.toList()*.toString() == ['d', 'e', 'f']
def ca3= new char[6]
assert fr.read(ca3, 3, 2) == 2 assert ca3.toList()*.toString() == ['\0', '\0', '\0', 'g', 'h', '\0']
fr.skip(20)
assert fr.read(ca3) == -1
if( fr.markSupported() ){
fr.reset()
assert fr.read() == 'a' as char
fr.mark(0) fr.read(new char[4])
fr.reset() assert fr.read() == 'b' as char
}
fr.close()
try{ fr.read(); assert 0 }catch(e){ assert e instanceof IOException }
}
new File('TestFile.txt').delete()
Closing Streams
When we write to an output stream or writer such as FileWriter, we should always close() it in some way:
def fw= new FileWriter('TestFile1.txt')
try{
fw.write('abc\r\ndefg')
throw new IOException('') }catch(e){ }
assert new File('TestFile1.txt').readLines().toList() == []
new File('TestFile1.txt').delete()
assert new File('TestFile1.txt').exists()
def fw2= new FileWriter('TestFile2.txt')
try{
try{
fw2.write('abc\r\ndefg')
throw new IOException('') }finally{
fw2.close() }
}catch(e){ }
assert new File('TestFile2.txt').readLines() == ['abc', 'defg']
new File('TestFile2.txt').delete()
assert ! new File('TestFile2.txt').exists()
try{
new File('TestFile3.txt').withWriter(){ w->
w.write('abc\r\ndefg')
throw new IOException('') }
}catch(e){ }
new File('TestFile3.txt').delete()
assert ! new File('TestFile3.txt').exists()
We can choose from many such methods to read and write characters to streams, where the stream is always closed automatically. Here's some methods which use a Reader and/or Writer. Although these examples use Files, all these methods work for other streamed resources also.
new File('TestFile1.txt').withWriter{ w->
w<< 'abc' << 'def' w.leftShift('ghi').leftShift('jkl') }
new File('TestFile1.txt').withWriter('unicode'){ w->
w<< 'abcdefghij'
}
new File('TestFile1.txt').withWriterAppend('unicode'){ w->
w<< 'klmnop' }
def fw= new FileWriter('TestFile1.txt')
fw.withWriter{ w->
['ab,cd\n' + 'efg\n' + 'hi,jk\n' + 'l', 'mn,op'].each{
w<< it
}
}
new File('TestFile1.txt').withReader{ r->
assert r.read() == 'a'
}
def list= []
new File('TestFile1.txt').eachLine{
list<< it
}
assert list == ['ab,cd', 'efg', 'hi,jk', 'lmn,op']
assert new File('TestFile1.txt').readLines() ==
['ab,cd', 'efg', 'hi,jk', 'lmn,op']
assert new File('TestFile1.txt').text ==
'ab,cd\n' + 'efg\n' + 'hi,jk\n' + 'lmn,op'
def fw2= new FileWriter('TestFile2.txt')
new File('TestFile1.txt').filterLine(fw2){ line->
! line.contains('g')
}
assert new File('TestFile2.txt').text ==
'ab,cd\r\n' + 'hi,jk\r\n' + 'lmn,op\r\n'
def fw2a= new FileWriter('TestFile2.txt')
new FileReader('TestFile1.txt').filterLine(fw2a){ line->
! line.contains('g')
}
assert new File('TestFile2.txt').text ==
'ab,cd\r\n' + 'hi,jk\r\n' + 'lmn,op\r\n'
def fr2= new FileReader('TestFile2.txt')
assert [fr2.readLine(), fr2.readLine()] == ['ab,cd', null]
fr2.close()
new FileReader('TestFile2.txt').withReader{ r->
def ca= new char[25]
r.read(ca)
assert ca.toList().join('').trim() == 'ab,cd\r\n' + 'hi,jk\r\n' + 'lmn,op'
}
def list2= []
new FileReader('TestFile2.txt').splitEachLine(','){ line->
list2<< line
}
assert list2 == [ ['ab', 'cd'], ['hi', 'jk'], ['lmn', 'op'] ]
def fw2b= new FileWriter('TestFile2.txt')
new FileReader('TestFile1.txt').transformLine(fw2b){ line->
if( line.contains(',') ) line += ',z'
line
}
assert new File('TestFile2.txt').text ==
'ab,cd,z\r\n' + 'efg\r\n' + 'hi,jk,z\r\n' + 'lmn,op,z\r\n'
def fw2c= new FileWriter('TestFile2.txt')
new FileReader('TestFile1.txt').transformLine(fw2c){ line->
if( line.contains(',') ) line += ',z'
line
}
assert new File('TestFile2.txt').text ==
'ab,cd,z\r\n' + 'efg\r\n' + 'hi,jk,z\r\n' + 'lmn,op,z\r\n'
def fw2d= new FileWriter('TestFile2.txt')
new FileReader('TestFile1.txt').transformChar(fw2d){ ch->
if(ch == ',') ch= '***'
ch
}
assert new File('TestFile2.txt').text ==
'ab***cd\n' + 'efg\n' + 'hi***jk\n' + 'lmn***op'
[new File('TestFile1.txt'), new File('TestFile2.txt')].each{ it.delete() }
Some methods which use an input and/or output stream which, although using Files in the examples, all work for other streamed resources also:
new File('TestFile1.txt').withOutputStream{ os->
os<< ([95, 96] as byte[]) os.leftShift( [97, 98, 99] as byte[] ) }
assert new File('TestFile1.txt').readBytes().toList() == [95, 96, 97, 98, 99]
def list= []
new File('TestFile1.txt').eachByte(){ b->
list<< b
}
assert list == [95, 96, 97, 98, 99]
new FileOutputStream('TestFile1.txt').withStream{ os->
os.write([100, 101, 102, 103] as byte[])
}
def list2= []
new FileInputStream('TestFile1.txt').eachByte(){ b->
list2<< b
}
assert list2 == [100, 101, 102, 103]
new File('TestFile1.txt').withInputStream{ is->
def ba= new byte[5]
is.read(ba)
assert ba == [100, 101, 102, 103, 0]
}
new FileInputStream('TestFile1.txt').withStream{ s->
def ba= new byte[5]
s.read(ba)
assert ba == [100, 101, 102, 103, 0]
}
assert new FileInputStream('TestFile1.txt').text == 'defg'
assert new FileInputStream('TestFile1.txt').getText('unicode') == '摥晧'
new FileInputStream('TestFile1.txt').withReader{ r->
assert r.read() == 'd'
}
new FileOutputStream('TestFile2.txt').withWriter('unicode'){ w->
w<< '我是法国人'
}
assert new FileInputStream('TestFile2.txt').getText('unicode') == '我是法国人'
new FileOutputStream('TestFile2.txt').withWriter{ w->
w<< new FileInputStream('TestFile1.txt')
w<< 2.495 << '\n' w<< [3,4,5]
}
assert new FileInputStream('TestFile2.txt').text == 'defg2.495\n' + '[3, 4, 5]'
def list3= []
new FileInputStream('TestFile2.txt').eachLine{ line->
list3<< line
}
assert list3 == ['defg2.495', '[3, 4, 5]']
new FileInputStream('TestFile2.txt').readLine() == 'defg2.495'
new FileInputStream('TestFile2.txt').readLines() == ['defg2.495', '[3, 4, 5]']
def fw3= new FileWriter('TestFile3.txt')
new FileInputStream('TestFile2.txt').filterLine(fw3){ line->
line.contains('g')
}
assert new File('TestFile3.txt').readLines() == ['defg2.495']
[ new File('TestFile1.txt'),
new File('TestFile2.txt'),
new File('TestFile3.txt')].each{ it.delete() }
Although the examples above are for files, they're all available for streams, readers, and writers around all other resources also.
Resource-specific Streams
When we met the FileInputStream, FileOutputStream, FileReader, and FileWriter in the above examples, we constructed them with a single String. We can also construct them with a file, and add an 'append' flag:
def fos= new FileOutputStream(new File('TestFile.txt'), true)
fos= new FileOutputStream(new File('TestFile.txt'), false) fos= new FileOutputStream(new File('TestFile.txt')) fos= new FileOutputStream('TestFile.txt', true) fos= new FileOutputStream('TestFile.txt', false) fos= new FileOutputStream('TestFile.txt')
def fis= new FileInputStream(new File('TestFile.txt'))
fis= new FileInputStream('TestFile.txt')
def fw= new FileWriter(new File('TestFile.txt'), true) fw= new FileWriter(new File('TestFile.txt'), true) fw= new FileWriter(new File('TestFile.txt')) fw= new FileWriter('TestFile.txt', true) fw= new FileWriter('TestFile.txt', false) fw= new FileWriter('TestFile.txt')
def fr= new FileReader(new File('TestFile.txt'))
fr= new FileReader('TestFile.txt')
There are many other streams, readers, and writers that wrap around specific resources. ByteArrayInputStream and ByteArrayOutputStream wrap around an array of bytes:
def bais= new ByteArrayInputStream( [33, 34, 35] as byte[] )
[33, 34, 35, -1].each{ assert bais.read() == it }
def bais2=
new ByteArrayInputStream( [33, 34, 35, 36, 37, 38, 39] as byte[], 2, 4 )
[35, 36, 37, 38, -1].each{ assert bais2.read() == it }
def baos= new ByteArrayOutputStream()
baos.write([100, 101, 102, 103] as byte[])
assert baos.size() == 4
assert baos.toByteArray().toList() == [100, 101, 102, 103]
def baos2= new ByteArrayOutputStream(10)
baos.writeTo( baos2 ) assert baos2.toByteArray().toList() == [100, 101, 102, 103]
assert baos2.toString() == 'defg'
assert baos2.toString('unicode') == '摥晧'
baos2.reset()
assert baos2.toByteArray().toList() == []
CharArrayReader and CharArrayWriter wrap around an array of chars:
def car= new CharArrayReader( ['a', 'b', 'c'] as char[] )
['a', 'b', 'c', -1].each{ assert car.read() == it }
def car2=
new CharArrayReader( ['a', 'b', 'c', 'd', 'e', 'f', 'g'] as char[], 2, 4 )
['c', 'd', 'e', 'f', -1].each{ assert car2.read() == it }
def caw= new CharArrayWriter()
caw.write(['a', 'b', 'c', 'd'] as char[])
assert caw.size() == 4
assert caw.toCharArray().toList() == ['a', 'b', 'c', 'd'].collect{ it as char }
def caw2= new CharArrayWriter(10)
caw.writeTo( caw2 ) assert caw2.toCharArray().toList() == ['a', 'b', 'c', 'd'].collect{ it as char }
assert caw2.toString() == 'abcd'
caw2.reset()
assert caw2.toCharArray().toList() == []
StringReader and StringWriter wrap around a StringBuffer:
def sr= new StringReader( 'abcde' )
['a', 'b', 'c', 'd', 'e', -1].each{ assert sr.read() == it }
def sw= new StringWriter()
sw= new StringWriter(10) sw.write( 'abcde' )
assert sw.buffer.toString() == 'abcde'
assert sw.toString() == 'abcde'
InputStreamReader and OutputStreamWriter are a reader and writer pair that forms the bridge between byte streams and character streams. An InputStreamReader reads bytes from an InputStream and converts them to characters using a character-encoding, either the default or one specified by name. Similarly, an OutputStreamWriter converts characters to bytes using a character-encoding and then writes those bytes to an OutputStream. In this example, we use a FileInputStream and FileOutputStream, but any InputStream or OutputStream could be used:
def wtr= new OutputStreamWriter(new FileOutputStream('TheOutput.txt'))
wtr<< 'abc'
wtr.close()
def rdr= new InputStreamReader(new FileInputStream('TheOutput.txt'))
def list= []
rdr.eachLine{ list<< it }
assert list == ['abc']
println System.getProperty("file.encoding")
wtr= new OutputStreamWriter(new FileOutputStream('TheOutput.txt'), 'unicode')
wtr<< 'def'
println wtr.encoding wtr.close()
rdr= new InputStreamReader(new FileInputStream('TheOutput.txt'), 'unicode')
println rdr.encoding
list= []
rdr.eachLine{ list<< it }
assert list == ['def']
The buffered streams, reader, and writer wrap around another, buffering the data read or written so as to provide for the efficient processing of bytes, characters, arrays, and lines. It's very useful for streams, readers, and writers whose input/output operations are costly, such as files.
def bos= new BufferedOutputStream(new FileOutputStream('TheOutput.txt'))
println bos.buf.size() bos= new BufferedOutputStream(new FileOutputStream('TheOutput.txt'), 16384)
assert bos.buf.size() == 16384
bos= new File('TheOutput.txt').newOutputStream()
def bis= new BufferedInputStream(new FileInputStream('TheOutput.txt'))
bis= new BufferedInputStream(new FileInputStream('TheOutput.txt'), 16384)
bis= new File('TheOutput.txt').newInputStream()
def bwtr= new BufferedWriter(new FileWriter('TheOutput.txt'))
bwtr= new BufferedWriter(new FileWriter('TheOutput.txt'), 16384)
bwtr= new File('TheOutput.txt').newWriter() bwtr= new File('TheOutput.txt').newWriter('unicode')
bwtr= new File('TheOutput.txt').newWriter(true) bwtr= new File('TheOutput.txt').newWriter('unicode', true)
def brdr= new BufferedReader(new FileReader('TheOutput.txt'))
brdr= new BufferedReader(new FileReader('TheOutput.txt'), 16384)
brdr= new File('TheOutput.txt').newReader() brdr= new File('TheOutput.txt').newReader('unicode')
brdr= new FileInputStream('TheOutput.txt').newReader()
def file= new File('TheOutput.txt')
def wtr= file.newWriter()
wtr.writeLine('abc')
wtr.writeLine('def')
wtr.newLine() wtr.close()
def rdr= file.newReader()
assert rdr.readLine() == 'abc' assert rdr.text == 'def' + '\r\n' + '\r\n'
A SequenceInputStream joins two other streams together:
def f1= new File('TheOutput1.txt'), f2= new File('TheOutput2.txt')
f1<< 'abcde'; f2<< 'fghij'
def is1= new FileInputStream(f1), is2= new FileInputStream(f2)
def sis= new SequenceInputStream(is1, is2)
assert sis.text == 'abcdefghij'
SequenceInputStream can also join three or more streams together using a Vector. See the upcoming tutorial on multi-threading for more on Vectors:
def f1= new File('TheOutput1.txt'),
f2= new File('TheOutput2.txt'),
f3= new File('TheOutput3.txt')
f1<< 'abc'; f2<< 'def'; f3<< 'ghij'
def list=[ new FileInputStream(f1),
new FileInputStream(f2),
new FileInputStream(f3) ]
def sis= new SequenceInputStream(new Vector(list).elements())
assert sis.text == 'abcdefghij'
A line-number reader keeps track of line numbers:
def w= new File('TheOutput.txt').newWriter()
w.writeLine('abc'); w.writeLine('defg'); w.close()
def lnr= new LineNumberReader(new FileReader('TheOutput.txt'))
lnr= new LineNumberReader(new FileReader('TheOutput.txt'), 16384)
assert lnr.lineNumber == 0
assert lnr.readLine() == 'abc'
assert lnr.lineNumber == 1
lnr.lineNumber= 4
assert lnr.readLine() == 'defg'
assert lnr.lineNumber == 5
A pushback input stream allows read input to be pushed back on:
def ba= [7, 8, 9, 10, 11, 12, 13] as byte[]
def pis= new PushbackInputStream(new ByteArrayInputStream(ba))
pis= new PushbackInputStream(new ByteArrayInputStream(ba), 1024)
def ba2= new byte[3]
pis.read(ba2)
assert ba2.toList() == [7, 8, 9]
pis.unread(2)
pis.read(ba2)
assert ba2.toList() == [2, 10, 11]
pis.unread([3, 4, 5, 6] as byte[])
pis.read(ba2)
assert ba2.toList() == [3, 4, 5]
pis.read(ba2)
assert ba2.toList() == [6, 12, 13]
A pushback reader provides a similar facility for characters:
def ca= ['g', 'h', 'i', 'j', 'k', 'l', 'm'] as char[]
def prdr= new PushbackReader(new CharArrayReader(ca))
prdr= new PushbackReader(new CharArrayReader(ca), 1024)
def ca2= new char[3]
prdr.read(ca2)
assert ca2.toList() == ['g', 'h', 'i'].collect{it as char}
prdr.unread('b' as int)
prdr.read(ca2)
assert ca2.toList() == ['b', 'j', 'k'].collect{it as char}
prdr.unread(['c', 'd', 'e', 'f'] as char[])
prdr.read(ca2)
assert ca2.toList() == ['c', 'd', 'e'].collect{it as char}
prdr.read(ca2)
assert ca2.toList() == ['f', 'l', 'm'].collect{it as char}
prdr.unread(['a', 'b', 'c', 'd', 'e', 'f', 'g'] as char[], 1, 4)
prdr.read(ca2)
assert ca2.toList() == ['b', 'c', 'd'].collect{it as char}
A DataOutputStream writes out Groovy structures as bytes, and a DataInputStream reads such bytes in as Groovy structures:
def baos= new ByteArrayOutputStream(30)
def dos= new DataOutputStream(baos)
assert dos.size() == 0
def bais= new ByteArrayInputStream( baos.buf )
def dis= new DataInputStream(bais)
dos.writeBoolean( true )
assert baos.toByteArray().toList() == [1] assert dis.readBoolean() == true
dos.writeByte( 200 ) assert baos.toByteArray().toList() == [1, -56]
assert dis.readByte() == -56
dis.reset() dis.skipBytes(1) assert dis.readUnsignedByte() == 200
baos.reset() dis.reset()
dos.writeBytes('abcdefg') assert baos.toByteArray() as List == [97, 98, 99, 100, 101, 102, 103]
dis.reset()
def ba= new byte[5]
dis.readFully(ba) assert ba as List == [97, 98, 99, 100, 101]
dis.reset()
ba= new byte[5]
dis.readFully(ba, 1, 2) assert ba as List == [0, 97, 98, 0, 0]
baos.reset(); dis.reset()
dos.writeChar('a' as int) assert baos.toByteArray() as List == [0, 97]
assert dis.readChar() == 'a'
baos.reset(); dis.reset()
dos.writeChars('ab') assert baos.toByteArray() as List == [0, 97, 0, 98]
baos.reset(); dis.reset()
dos.writeShort(5000) assert baos.toByteArray() as List == [19, -120] && 20*256 - 120 == 5000
assert dis.readShort() == 5000
dis.reset()
dis.readUnsignedShort() == 5000 baos.reset(); dis.reset()
dos.writeInt(5000) assert baos.toByteArray() as List == [0, 0, 19, -120]
assert dis.readInt() == 5000
baos.reset(); dis.reset()
dos.writeLong(5000) assert baos.toByteArray() as List == [0, 0, 0, 0, 0, 0, 19, -120]
assert dis.readLong() == 5000
baos.reset(); dis.reset()
dos.writeDouble(123.456)
println baos.toByteArray() as List
assert dis.readDouble() == 123.456d
baos.reset(); dis.reset()
dos.writeFloat(123.456f)
println baos.toByteArray() as List
assert dis.readFloat() == 123.456f
baos.reset(); dis.reset()
dos.writeUTF('abc')
assert baos.toByteArray() as List == [0, 3, 97, 98, 99]
assert dis.readUTF() == 'abc'
dis.reset()
assert DataInputStream.readUTF(dis) == 'abc'
We'll meet more different types of streams, readers, and writers in the tutorials on Inheritance, Networking, Multi-threading, and others coming up.
|
|