Scala文件操作示例代码讲解
作者:Maverick_曲流觞
1. 读取数据
在Scala语言的 Source单例对象 中, 提供了一些非常便捷的方法, 从而使开发者可以快速的从指定数据源(文本文件, URL地址等)中获取数据, 在使用 Source单例对象 之前, 需要先导包, 即 import scala.io.Source.
1.1 按行读取
我们可以以 行 为单位, 来读取数据源中的数据, 返回值是一个 迭代器类型的对象 . 然后通过 toArray, toList
方法, 将这些数据放到数组或者列表中即可.
注意: Source类扩展自Iterator[Char]
格式
//1. 获取数据源文件对象. val source:BufferedSource = Source.fromFile("数据源文件的路径","编码表") //2. 以行为单位读取数据. val lines:Iterator[String] = source.getLines() //3. 将读取到的数据封装到列表中. val list1:List[String] = lines.toList //4. 千万别忘记关闭Source对象. source.close()
需求
在当前项目下创建data文件夹, 并在其中创建1.txt文本文件, 文件内容如下:
好好学习, 天天向上!
Hadoop, Zookeeper, Flume, Spark
Flink, Sqoop, HBase
以行为单位读取该文本文件中的数据, 并打印结果.
参考代码
import scala.io.Source object ClassDemo01 { def main(args: Array[String]): Unit = { //1. 获取数据源对象. val source = Source.fromFile("./data/1.txt") //2.通过getLines()方法, 逐行获取文件中的数据. var lines: Iterator[String] = source.getLines() //3. 将获取到的每一条数据都封装到列表中. val list1 = lines.toList //4. 打印结果 for(s <- list1) println(s) //5. 记得关闭source对象. source.close() } }
1.2 按字符读取
Scala还提供了 以字符为单位读取数据 这种方式, 这种用法类似于迭代器, 读取数据之后, 我们可以通过 hasNext(),next()方法 , 灵活的获取数据.
Scala使用source.buffered方法按字符读取文件
什么是source.buffered方法
source.buffered方法是scala.io.Source类的一个成员方法,它可以将一个Source对象转换为一个BufferedSource对象。BufferedSource对象是一个实现了BufferedIterator特质的对象,它可以提供一个缓冲区,用来存储未被消费的元素。这样可以提高读取文件的效率,也可以方便地查看下一个元素而不消费它。
如何使用source.buffered方法
要使用source.buffered方法,首先需要导入scala.io.Source类,并创建一个Source对象,然后调用其buffered方法,得到一个BufferedSource对象。例如,以下代码创建了一个从文件中获取的Source对象,并转换为一个BufferedSource对象:
import scala.io.Source // 创建一个从文件中获取的Source对象 val source = Source.fromFile("demo.txt") // 调用buffered方法,得到一个BufferedSource对象 val buffered = source.buffered
然后,可以使用BufferedSource对象的各种方法来按字符读取文件。例如,以下代码使用head方法来查看下一个字符,使用next方法来消费下一个字符,并打印出来:
// 使用head方法查看下一个字符 println(buffered.head) // 使用next方法消费下一个字符 println(buffered.next()) // 继续使用head方法查看下一个字符 println(buffered.head) // 继续使用next方法消费下一个字符 println(buffered.next())
最后,需要关闭Source对象,释放资源:
// 关闭Source对象source.close()
一个示例
下面给出一个完整的示例,演示如何使用source.buffered方法来按字符读取文件,并打印出来。
首先,准备一个文本文件demo.txt,内容如下:
Hello, Scala!
你好,Scala!
然后,编写以下代码:
import scala.io.Source // 创建一个从文件中获取的Source对象,并转换为一个BufferedSource对象 val buffered = Source.fromFile("demo.txt").buffered // 循环判断是否有下一个元素 while (buffered.hasNext) { // 打印出当前元素,并消费掉 print(buffered.next()) } // 关闭Source对象 buffered.close()
最后,运行程序,可以看到输出结果如下:
Hello, Scala!
你好,Scala!
1.3 读取词法单元和数字
所谓的词法单元指的是 以特定符号间隔开的字符串 , 如果数据源文件中的数据都是 数字形式的字符串 , 我们可以很方便的从文件中直接获取这些数据, 例如:
10 2 5
11 2
5 1 3 2
格式
//1. 获取数据源文件对象. val source:BufferedSource = Source.fromFile("数据源文件的路径","编码表") //2. 读取词法单元. // \s表示空白字符(空格, \t, \r, \n等) val arr:Array[String] = source.mkString.split("\\s+") //3. 将字符串转成对应的整数 val num = strNumber.map(_.toInt) //4. 千万别忘记关闭Source对象. source.close()
参考代码
将上面的数字存入2.txt
import scala.io.Source object ClassDemo03 { def main(args: Array[String]): Unit = { val source = Source.fromFile("./data/2.txt") // \s表示空白字符(空格, \t, \r, \n等) val strNumber = source.mkString.split("\\s+") //将字符串转成对应的整数 val num = strNumber.map(_.toInt) for(a <- num) println(a + 1) } }
1.4 从URL或者其他源读取数据
Scala中提供了一种方式, 可以让我们直接从指定的URL路径, 或者其他源(例如: 特定的字符串)中直接读取数据。
格式 从URL地址中读取数据
//1. 获取数据源文件对象. val source = Source.fromURL("https://www.csdn.net") //2. 将数据封装到字符串中并打印. println(source.mkString)
从其他源读取数据
//1. 获取数据源文件对象. val str = Source.fromString("CSDN") println(str.getLines())
需求
- 读取
https://www.csdn.net
页面的数据, 并打印结果. - 直接读取字符串
CSDN
, 并打印结果.
参考代码
import scala.io.Source object ClassDemo04 { def main(args: Array[String]): Unit = { val source = Source.fromURL("https://www.csdn.net") println(source.mkString) val str = Source.fromString("CSDN") println(str.getLines()) } }
1.5 读取二进制文件
Scala没有提供读取二进制文件的方法, 我们需要通过Java类库来实现.
需求 已知项目的data文件夹下有 05.png
这张图片, 请读取该图片数据, 并将读取到的字节数打印到控制台上. 参考代码
object ClassDemo05 { def main(args: Array[String]): Unit = { val file = new File("./data/04.png") val fis = new FileInputStream(file) val bys = new Array[Byte](file.length().toInt) fis.read(bys) fis.close() println(bys.length) } }
2. 写入文件
2.1 使用java.io.PrintWriter类
java.io.PrintWriter类是Java提供的一个用于写入文本文件的类。它可以接受一个文件、一个输出流或一个字符串作为参数,然后通过调用其write方法来向文件中写入数据。例如,以下代码可以向文件hello.txt中写入一行文本:
import java.io._ val pw = new PrintWriter(new File("hello.txt")) pw.write("Hello, world") pw.close()
使用PrintWriter类写入文件时,有两个需要注意的问题:
- PrintWriter类不会抛出异常,而是设置一个布尔标志来表示是否发生了错误。可以通过调用其checkError方法来检查是否有错误发生。
- PrintWriter类默认使用平台的字符编码,这可能导致不同平台之间的不一致。可以通过指定字符编码来避免这个问题。例如,以下代码使用了UTF-8编码:
val pw = new PrintWriter(new File("hello.txt"), "UTF-8")
2.2 使用java.io.FileWriter类
java.io.FileWriter类是Java提供的另一个用于写入文本文件的类。它是OutputStreamWriter的子类,可以接受一个文件或一个字符串作为参数,然后通过调用其write方法来向文件中写入数据。例如,以下代码可以向文件test.txt中写入一行文本:
import java.io._ val file = new File("test.txt") val bw = new BufferedWriter(new FileWriter(file)) bw.write("We are learning Scala programming language") bw.close()
使用FileWriter类写入文件时,有两个需要注意的问题:
- FileWriter类会抛出IOExceptions异常,所以需要使用try-catch-finally语句来处理异常和关闭文件。
- FileWriter类默认使用平台的字符编码,这可能导致不同平台之间的不一致。FileWriter类没有提供指定字符编码的构造器,所以如果需要指定字符编码,可以使用OutputStreamWriter类代替。例如,以下代码使用了UTF-8编码:
val file = new File("test.txt") val bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8")) bw.write("We are learning Scala programming language") bw.close()
2.3 使用java.io.FileOutputStream类
FileOutputStream是Java提供的一个用于写入二进制文件的类,它是OutputStream的子类,可以接受一个文件或一个字符串作为参数,然后通过调用其write方法来向文件中写入字节数据。例如,以下代码可以向文件data.bin中写入一个整数:
import java.io._ val file = new File("data.bin") val fos = new FileOutputStream(file) fos.write(42) fos.close()
FileOutputStream类会抛出IOExceptions异常,所以需要使用try-catch-finally语句来处理异常和关闭文件。
已收到消息. FileOutputStream是Java提供的一个用于写入二进制文件的类,它是OutputStream的子类,可以接受一个文件或一个字符串作为参数,然后通过调用其write方法来向文件中写入字节数据。例如,以下代码可以向文件data.bin中写入一个整数: scala import java.io._ val file = new File("data.bin") val fos = new FileOutputStream(file) fos.write(42) fos.close()
FileOutputStream类会抛出IOExceptions异常,所以需要使用try-catch-finally语句来处理异常和关闭文件。
2.4 几种写入的区别
因为FileOutputStream类有两种构造器,一种接受一个文件对象作为参数,一种接受一个字符串作为参数。如果接受一个字符串作为参数,那么它会自动创建一个文件对象。所以,以下两种写法是等价的:
val pw = new FileOutputStream(new File("./data/3.txt")) val pw = new FileOutputStream("./data/3.txt")
但是,如果使用PrintWriter类或FileWriter类,那么就必须使用一个文件对象作为参数,不能使用一个字符串。所以,以下两种写法是不等价的:
val pw = new PrintWriter(new File("./data/3.txt")) val pw = new PrintWriter("./data/3.txt") // 错误
2.5 使用第三方库
除了Java提供的类之外,还有一些第三方库可以用来写入文件,例如Apache Commons IO1和os-lib2等。这些库通常提供了更简洁和高效的API,也支持更多的功能和格式。例如,以下代码使用了Apache Commons IO库的FileUtils类来写入文件,并指定了字符编码:
import org.apache.commons.io.FileUtils FileUtils.writeStringToFile(new File("test.txt"), "Hello, world", "UTF-8")
以下代码使用了os-lib库的os.File对象来写入文件,并返回一个字节长度:
import os._ os.write(os.pwd / "test.txt", "Hello, world")
3. Scala序列化和反序列化
3.1 什么是序列化和反序列化
在Scala中,如果想将对象传输到其他虚拟机,或者临时存储,就可以通过序列化和反序列化来实现了。
- 序列化:把对象写到文件中的过程。
- 反序列化:从文件中加载对象的过程。
3.2 如何实现序列化和反序列化
一个类的对象要想实现序列化和反序列化操作,则该类必须继承Serializable特质。Serializable特质是一个空特质,它没有任何方法和字段,只是用来标记一个类是否可以被序列化和反序列化。
要实现序列化操作,可以使用java.io.ObjectOutputStream类,它是一个用于写入对象的输出流。它可以接受一个输出流作为参数,然后通过调用其writeObject方法来写入对象。
要实现反序列化操作,可以使用java.io.ObjectInputStream类,它是一个用于读取对象的输入流。它可以接受一个输入流作为参数,然后通过调用其readObject方法来读取对象。
3.3 一个示例
下面给出一个简单的示例,演示如何使用case class和object来实现序列化和反序列化操作。
首先,定义一个case class Person,属性为姓名和年龄。注意,case class会自动继承Serializable特质,所以不需要显式地写出来。
// 定义一个case class Person case class Person(var name: String, var age: Int)
然后,创建Person样例类的对象p,并通过序列化操作将对象p写入到项目下的data文件夹下的4.txt文本文件中。
// 创建Person样例类的对象p val p = Person("张三", 23) // 创建一个ObjectOutputStream对象,传入一个输出流作为参数 val oos = new ObjectOutputStream(new FileOutputStream("./data/4.txt")) // 调用writeObject方法,将对象p写入到输出流中 oos.writeObject(p) // 关闭输出流 oos.close()
最后,通过反序列化操作从项目下的data文件夹下的4.txt文件中,读取对象p,并打印出来。
// 创建一个ObjectInputStream对象,传入一个输入流作为参数 val ois = new ObjectInputStream(new FileInputStream("./data/4.txt")) // 调用readObject方法,从输入流中读取对象,并转换为Person类型 var p: Person = ois.readObject().asInstanceOf[Person] // 打印对象p println(p) // 关闭输入流 ois.close()
3.4 使用注解
除了使用Serializable特质之外,还可以使用@SerialVersionUID注解来指定一个版本号。这样可以避免不同版本的类之间的兼容性问题。例如,以下代码定义了一个普通的类,并添加了注解:
import java.io._ // 使用@SerialVersionUID注解指定版本号为100L @SerialVersionUID(100L) // 定义一个普通的类,并继承Serializable特质 class Person(var name: String, var age: Int) extends Serializable
然后,可以使用相同的方式进行序列化和反序列化操作:
val p = new Person("张三", 23) val oos = new ObjectOutputStream(new FileOutputStream("./data/4.txt")) oos.writeObject(p) oos.close() val ois = new ObjectInputStream(new FileInputStream("./data/4.txt")) var p: Person = ois.readObject().asInstanceOf[Person] println(p)
案例-学员成绩表
概述
已知项目下的data文件夹的student.txt文本文件中, 记录了一些学员的成绩, 如下: 格式为: 姓名 语文成绩 数学成绩 英语成绩
张三 37 90 100
李四 90 73 81
王五 60 90 76
赵六 89 21 72
田七 100 100 100
按照学员的总成绩降序排列后, 按照 姓名 语文成绩 数学成绩 英语成绩 总成绩 的格式, 将数据写到项目下的data文件夹的stu.txt文件中.
** 步骤**
- 定义样例类Person, 属性为: 姓名, 语文成绩, 数学成绩, 英语成绩, 且该类中有一个获取总成绩的方法.
- 读取指定文件(./data/student.txt)中所有的数据, 并将其封装到List列表中.
- 定义可变的列表ListBuffer[Student], 用来记录所有学生的信息.
- 遍历第二步获取到的数据, 将其封装成Person类的对象后, 并添加到ListBuffer中.
- 对第4步获取到的数据进行排序操作, 并将其转换成List列表.
- 按照指定格式, 通过BufferWriter将排序后的数据写入到目的地文件中(./data/stu.txt)
- 关闭流对象.
参考代码
object ClassDemo08 { //1. 定义样例类Person, 属性: 姓名, 语文成绩, 数学成绩, 英语成绩, 且该类中有一个获取总成绩的方法. case class Student(name:String, chinese:Int, math:Int, english:Int) { def getSum() = chinese + math + english } def main(args: Array[String]): Unit = { //2. 获取数据源文件对象. val source = Source.fromFile("./data/student.txt") //3. 读取指定文件(./data/student.txt)中所有的数据, 并将其封装到List列表中. var studentList: Iterator[List[String]] = source.getLines().map(_.split(" ")).map(_.toList) //4. 定义可变的列表ListBuffer[Student], 用来记录所有学生的信息. val list = new ListBuffer[Student]() //5. 遍历第二步获取到的数据, 将其封装成Person类的对象后, 并添加到ListBuffer中. for(s <- studentList) {list += Student(s(0), s(1).toInt, s(2).toInt, s(3).toInt)} //6. 对第5步获取到的数据进行排序操作, 并将其转换成List列表. val sortList = list.sortBy(_.getSum()).reverse.toList //7. 按照指定格式, 通过BufferWriter将排序后的数据写入到目的地文件(./data/stu.txt) val bw = new BufferedWriter(new FileWriter("./data/stu.txt")) for(s <- sortList) bw.write(s"${s.name} ${s.chinese} ${s.math} ${s.english}${s.getSum()}\r\n") //8. 关闭流对象 bw.close() } }
到此这篇关于Scala文件操作示例代码讲解的文章就介绍到这了,更多相关Scala文件操作内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!