Scala文件操作
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
- 以行为单位读取该文本文件中的数据, 并打印结果.
参考代码
importscala.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对象:
importscala.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!
然后,编写以下代码:
importscala.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
importscala.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
, 并打印结果.
参考代码
importscala.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中写入一行文本:
importjava.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中写入一行文本:
importjava.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中写入一个整数:
importjava.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类来写入文件,并指定了字符编码:
importorg.apache.commons.io.FileUtils
FileUtils.writeStringToFile(new File("test.txt"),"Hello, world","UTF-8")
以下代码使用了os-lib库的os.File对象来写入文件,并返回一个字节长度:
importos._
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 Personcaseclass Person(var name:String,var age:Int)
然后,创建Person样例类的对象p,并通过序列化操作将对象p写入到项目下的data文件夹下的4.txt文本文件中。
// 创建Person样例类的对象pval 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注解来指定一个版本号。这样可以避免不同版本的类之间的兼容性问题。例如,以下代码定义了一个普通的类,并添加了注解:
importjava.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, 属性: 姓名, 语文成绩, 数学成绩, 英语成绩, 且该类中有一个获取总成绩的方法.caseclass 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()}}
版权归原作者 Maverick_曲流觞 所有, 如有侵权,请联系我们删除。