你好,游客 登录
背景:
阅读新闻

Scala I/O接口示例

[日期:2021-09-27] 来源:  作者: [字体: ]

本文来自艾叔编著的《零基础快速入门Scala》免费电子书,添加文末艾叔微信,获取完整版的PDF电子书

第8章  Scala 编程示例

本章以示例形式来说明Scala各类API接口的使用,包括Scala I/O接口、正则表达式、Scala多线程接口和Scala网络编程,具体如下。

8.1  Scala I/O接口示例

8.1.1  文本文件读取单个字符

本节演示使用文件源来读取单个字符。

1行,引入Source所在的package

2行,创建“/etc/hosts”文件源;

3行,调用srcbuffered方法,获得单个字符的迭代器iteriter的类型是BufferedIterator[Char],利用iter可以访问源文件的每个字符

4~8行,利用iter读取源文件的每个字符,并打印;

10行,关闭src

      1 import scala.io.Source

      2 val src = Source.fromFile("/etc/hosts")

      3 val iter = src.buffered

      4 var num=0

      5 while(iter.hasNext){

      6   num += 1

      7   println(num + " " + iter.next)

      8 }

      9

     10 src.close()

注意

1)单个字符采用Char存储,Char2个字节;

2)iter.next返回指针当前所指向的Char,并移动指针到下一个Char。如果希望返回当前Char,但不指向下一个Char的话,可以使用iter.head

8.1.2  文本文件读取行

本示例将演示如何从文本文件中按行读取数据。

1行,引入Source所在的package

2行,使用SourcefromFile创建文件源(File Sourcesrc,输入参数是文件路径/etc/profilesrc的类型是BufferedSource,它提供了很多便捷的方法,来创建访问文件的迭代器;

3行,利用srcgetLines方法,创建一个按行访问源文件的迭代器iter

4~8行,利用iter依次访问源文件的每一行,并打印,每行以行号开头;

9行,关闭src,文件源打开后,要记得关闭。

      1 import scala.io.Source

      2 val src = Source.fromFile("/etc/profile")

      3 val iter = src.getLines()

      4 var lineNum=1

      5 while(iter.hasNext){

      6   println(lineNum + " " + iter.next())

      7   lineNum += 1l

      8 }

      9 src.close()

如果要将整个文件内容,转换成一个字符串,即String类型,可以使用mkString方法,如下。

val str = src.mkString

8.1.3  文本文件转成Array处理

如果文件不是很大,可以将其转换成Array,在内存直接处理。

3行,调用文件源srctoArray方法,将源文件(“/etc/hosts”)转换成Array[Char]

4~6行,遍历ar,每个元素是一个Char,代表源文件中的一个字符。

      1 import scala.io.Source

      2 val src = Source.fromFile("/etc/hosts")

      3 val ar = src.toArray

      4 for(c <- ar){

      5   println(c)

      6 }

      7 src.close()

8.1.4  文本文件直接用map处理

我们可以使用map函数,直接处理文件中的每个字符。

例如,将/etc/hosts”中的每个字符转换成大写,代码如下。

3行,在src上,直接用map函数,将每个字符转换成大写,_���表“/etc/hosts”中的每个字符;

4行,对迭代器mapIter调用mkString,转换成一个字符串String

5行,打印转换后的字符串。

      1 import scala.io.Source

      2 val src = Source.fromFile("/etc/hosts")

      3 val mapIter = src.map(_.toUpper)

      4 val str = mapIter.mkString

      5 println(str)

      6 src.close()

8.1.5  url读取文件

我们可以利用Source.fromURL可以读取网页,示例代码和说明如下。

2行,调用Source.fromURL,传入网页地址,注意网址前面要加入http://

3行,将读取的网页内容转换成字符串。

      1 import scala.io.Source

      2 val src = Source.fromURL("http://www.baidu.com")

      3 val str = src.mkString

      4 println(str)

      5 src.close()

8.1.6  从标准输入(stdin)读取

我们可以利用Source.stdin可以从标准输入(键盘)读取内容,示例代码和说明如下。

2行,调用Source.stdin获得BufferedSource引用,它代表标准输入,一般情况下,标准输入指键盘;

3行,调用src.getLines()获得String迭代器,赋值给lines

4~6行,当用户每在控制台输入1行数据,lines.hasNext就会返回变成true(否则,会一直停留在第4行),lines.next会获得当前输入的数据,打印,循环。

      1 import scala.io.Source

      2 val src = Source.stdin

      3 val lines = src.getLines()

      4 while(lines.hasNext){

      5   println(lines.next)

      6 }

如果是读取单个字符的话,则只需要将第3行删除即可。

8.1.7  将字符串写入文件

我们可以使用PrintWriter可以将字符串写入文件,实现文本文件的存储。

示例代码如下,实现了复制/etc/hosts”到“/tmp/hosts”的功能,其中就包括了文件的读取和写入。

1行,引入Source所在的package

2行,创建源文件对象,源文件路径为“/etc/hosts”;

3行,获得行迭代器lines

4行,引入PrintWriter所在的package

5行,创建PrintWriter对象out,传入要输出的文件路径“/tmp/hosts”;

6~8行,将读取的每行内容,通过out.println,输出到“/tmp/hosts”文件中;

9行,关闭out引用;

10行,关闭src引用。

      1 import scala.io.Source

      2 val src = Source.fromFile("/etc/hosts")

      3 val lines = src.getLines()

      4 import java.io.PrintWriter

      5 val out = new PrintWriter("/tmp/hosts")

      6 for(l <- lines){

      7   out.println(l)

      8 }

      9 out.close()

     10 src.close()

注意:

1)srcout一定要记得close

2)使用out.append可以将字符串追加到文件末尾。

8.1.8  文件的二进制读取和写入

所有的文件,不管是文本文件,视频文件还是其它文件,从本质上讲,都是一个字节数组:Array[Byte](注意,不是Array[Char]Char2字节,Byte1字节),至于这些文件中的具体内容,则和文件的编码方式以及文件格式有关。

Scala使用FileInputStream可以将文件原封不动的读取到一个Array[Byte]中,这就是所谓的二进制读取。使用FileOutputStream可以将Array[Byte]中的内容存储为一个文件,也就是二进制写入。

下面的代码将/bin/cp”读取到Array[Byte]数组,然后又将此数组的内容写入到/tmp目录,存储为cp文件。

1行,引入FileInputStreamFileOutputStream所在的package

2行,传入文件绝对路径“/bin/cp”,创建File对象;

3行,传入file引用,创建FileInputStream对象,赋值给in

4行,根据文件的大小,创建对应大小的字节数组fileBytes

5行,使用in.read,读取整个文件内容,存储到fileBytes中;

6行,关闭文件输入流in

8行,传入文件绝对路径“/tmp/cp”,创建临时File对象outFile

9行,传入outFile,创建文件输出流对象out

10行,使用outfileBytes的内容写入文件“/tmp/cp”;

11行,别忘了关闭文件输出流out

      1 import java.io.File

      2 val file = new File("/bin/cp")

      3 val in = new FileInputStream(file)

      4 val fileBytes = new Array[Byte](file.length().toInt)

      5 in.read(fileBytes)

      6 in.close()

      7

      8 val outFile = new File("/tmp/cp")

      9 val out = new FileOutputStream(outFile)

     10 out.write(fileBytes)

     11 out.close()

程序运行完毕,可以diff /bin/cp /tmp/cp,如果完全相等,则说明读取和写入都没有问题。

8.1.9  序列化和反序列化

序列化的作用是将一个对象的内容转换成Byte数组,这样做的好处有:1. 实现对象的持久化,可以将此Byte数组存储成一个文件,待需要时,再读取该文件,通过反序列化,将Byte数组转换成对象,这样,对象就可以持久存在,而不只是存在于内存中;2. 实现对象的传输,同样的道理,发送方对Byte数组通过网络传输,接收方对Byte数组进行反序列化,恢复成对象。

Scala中的序列化和反序列化调用的也是Java库中的接口。

例子代码如下,实现了将Person对象序列化,存储成/tmp/p.obj文件,然后读取p.obj,反序列化,再调用对象方法进行验证。

首先,定义Person类,Person扩展了Serializable,是因为它要进行序列化。

      1 class Person(val id: String, val name: String) extends Serializable {

      2   def say() = {println("Hello, I am "  + name)}

      3 }

将创建Person对象,赋值给p,将此对象序列化后,存储为文件。

1行,创建Person对象;

2行,引入序列化和持久化所在的package,这是javapackage,下划线_是通配符,表示引入java.io下所有的类;

3行,创建一个ObjectOutputStream对象out,它可以将一个对象存储为文件,输入参数是一个FileOutputStream对象,该对象的参数是要存储文件的路径“/tmp/p.obj”;

4行,调用out.writeObject,将Person对象存储为文件,这个过程就包括序列化和持久化;

5行,关闭out

      1 val p = new Person("320101201003210029", "tom")

      2 import java.io._

      3 val out = new ObjectOutputStream(new FileOutputStream("/tmp/p.obj"))

      4 out.writeObject(p)

      5 out.close()

验证,读取/tmp/p.obj”,反序列化称为Person对象,调用say()方法进行验证。

1行,创建一个ObjectInputStream对象in,其构造函数参数是一个FileInputStream对象,该对象的参数是p.obj的绝对路径“/tmp/p.obj”;

2行,利用in.readObject()读取“/tmp/p.obj”文件,利用asInstanceOf[Person],将其转换为Person对象,赋值给readP

3行,关闭in

4行,验证,调用readP.say方法,如果成功,则说明反序列化成功。

      1 val in = new ObjectInputStream(new FileInputStream("/tmp/p.obj"))

      2 val readP = in.readObject().asInstanceOf[Person]

      3 in.close()

      4 readP.say()

注意

  • 如果要对同一对象进行序列化和持久化,要注意对该对象的持久化文件版本进行编号;
  • 如果序列化和反序列化都是通过ScalaJava)完成,则无需考虑字节序问题;
  • asInstanceOf要慎用,必须要很清楚当前引用所指对象的真实类型,否则,转换会报错。

 

加艾叔微信,加入Linux(Shell+Zabbix)、大数据(Spark+Hadoop)、云原生(Docker+Kubernetes)技术交流群

 

 

 

 

关注艾叔公众号,获取更多一手信息

 

 

收藏 推荐 打印 | 阅读:
相关新闻