Sunday, August 19, 2012

Scala-IO Core: Seekable

At the same level of abstraction as Input and Output is the fine trait called Seekable.  As the name implies it provides random access style methods for interacting with a resource.  The example that comes immediately to mind is a random access file.

The design of Seekable largely mimics the scala.collection.Seq patch and insert methods.  Not much more to say beyond getting into some examples:
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
import scalax.io._
import java.io.File
 
// For this example lets explicitly specify a codec
implicit val codec = scalax.io.Codec.UTF8
 
val file: Seekable =  Resource.fromFile(new File("scala-io.out"))
 
// delete all data from the file
// or more specifically only keep the data from the beginning of the
// file up to (but not including) the first byte
file truncate 0
 
val seedData = "Entering some seed data into the file"
file write seedData
 
// verify seed data was correctly written (in REPL)
file.string
 
 
// write "first" after character 9
// if the file is < 9 characters an underflow exception is thrown
// if the patch extends past the end of the file then the file is extended
// Note: The offset is always dependent on type of data being written
// for example if the data written is a string it will be 9 characters
// if the data is bytes it will be 9 bytes
file patch (9, "first",OverwriteAll)
 
// dumping to REPL will show: Entering firstseed data into the file
file.string
 
// patch at position 0 and replace 100 bytes with new data
file.patch(0,seedData, OverwriteSome(100))
 
// dumping to REPL will show the unchanged seedData once again
file.string
 
// Overwrite only 4 bytes starting at bytes 9.
// the extra bytes will be inserted
// In other words the "some" word of seed data will
// be replaced with second
// Warning: This is an overwrite and an insert
// inserts are expensive since it requires copying the data from
// the index to end of file.  If small enough it is done in
// memory but a temporary file is required for big files.
file.patch(9,"second".getBytes(), OverwriteSome(4))
 
// dumping to REPL will show: Entering second seed data into the file
file.string
 
// reset file
file.patch(0,seedData, OverwriteSome(100))
 
// Replace 9 bytes with the 5 bytes that are provided
// In other words: replace "some seed" with "third"
file.patch(9,"third".getBytes(), OverwriteSome(9))
 
// dumping to REPL will show: Entering third data into the file
file.string
 
// reset file
file.patch(0,seedData, OverwriteSome(100))
 
// Insert a string at start of file
file.insert(0, "newInsertedData ")
 
// dumping to REPL will show: newInsertedData Entering some seed data into the file
file.string
 
// reset file
file.patch(0,seedData, OverwriteSome(100))
 
//add !! to end of file
file.append("!!")
 
// dumping to REPL will show: Entering some seed data into the file!!
file.string
IMPORTANT: Each time truncate() or patch or insert is called a new connection to the file is opened and closed. The Processor API is to be used to perform multiple operations within one connection.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import scalax.io._
import java.io.File
 
val file: Seekable =  Resource.fromFile(new File("scala-io.out"))
 
for {
  p <- file.seekableProcessor
  seekable = p.asSeekable
} {
  seekable truncate 0
  seekable write "hi"
  seekable append " world"
 
  // one can do patch, insert etc...
  // or move the cursor to the correct position and write
  // this is essentially a patch(0, "Hi", OverwriteAll)
  seekable.position = 0
  seekable write "Hi"
 
  seekable patch (3, "W", OverwriteAll)
 
  // dumping to REPL will show: Hi World
  println (seekable.string)
}

No comments:

Post a Comment