Scala 文件和正则表达式 快学Scala 第九章习题答案

0.重点

  • Source.fromFile(…).getLines.toArray 输出文件的所有行
  • Source.fromFile(…).getlines.mkString 以字符串形式输出文件内容
  • 将字符串转换为数字,可以用toInt或toDouble方法
  • 使用Java的PrintWriter来写入文本文件
  • “正则”.r 是一个Regex对象
  • 如果你的正则表达式包含反斜杠或引号,用"""..."""
  • 如果正则模式包含分组,你可以用如下语法来提取它们的内容 for (regex(变量1,...变量n) <- 字符串

1.文件操作

Scala 文件操作

7.访问目录

目前Scala并没有正式的用来访问某个目录中的所有文件,或者递归的遍历所有目录的类。
替代方案:

import java.io.File
def subdirs(dir:File):Iterator[File] = {
    val children = dir.listFiles.filter(_.isDirectory)
    children.toIterator ++ children.toIterator.flapMap(subdirs _)
}

//利用这个函数,可以这样访问所有的子目录:
for(d <- subdirs(dir)) 处理 d

如果用的是Java7,你也可以用java.nio.file.Files类的walkFileTree方法。该类用到了FileVisitor接口。在Scala中,我们通常喜欢用函数对象来指定工作内容,而不是接口(虽然在本例中接口可以有更细粒度的控制)。
以下隐式转换让函数可以与接口相匹配:

import java.nio.file._
implicit def makeFileVisitor(f:(Path)=>Unit) = new SimpleFileVisitor[Path]{
    override def visitFile(p:Path, attrs:attribute.BasicFileAttributes)={
        f(p)
        FileVisitResult.CONTINUE
    }
}
Files.walkFileTree(dir.toPath, (f:Path) => println(f))

8.序列化

在Java中,我们用序列化来将对象传输到其他虚拟机,或临时存储。
对于长期存储而言,序列化可能会比较笨拙——随着类的演进更新,处理不同版本间的对象是很繁琐的一件事。

//Java声明一个可被序列化的类
public class Person implements java.io.Serializable{
    private static final long serialVersionUID = 42L;
    ...
}
//Scala声明一个可被序列化的类
@SerialVersionUID(42L) class Person extends Serializable
// Serializable trait 定义在scala包,因此不需要显示引入。
// 如果能接受缺省的ID的话,也可略去@SerialVersionUID注解

按照常规的方式对对象进行序列化和反序列化

val fred = new Person(...)
import java.io._
val out = new ObjectOutputStream(new FileOutputStream("/tmp/test.obj"))
out.writeObject(fred)
out.close()
val in = new ObjectInputStream(new FileInputStream("/tmp/test.obj"))
val savedFred = in.readObject().asInstanceOf[Person]
//Scala集合类都是可序列化的,因此你可以把它们用做你的可序列化类的成员:
class Person extends Serializable{
    private val friends = new ArrayBuffer[Person]  //OK——ArrayBuffer是可序列化的
    ...
}

9.进程控制

scala.sys.process包提供了用于与shell程序交互的工具。
如:

import sys.process._
"ls -al .." ! #显示上层目录的所有文件

sys.process包含了一个从字符串到ProcessBuilder对象的隐式转换。!操作符执行的就是这个ProcessBuilder对象。
!操作符返回的是被执行程序的返回值:
程序成功执行的话就是0,否则就是错误的非0值。
还可以用!!,!!会以字符串的形式返回值:

val result = "ls -al .." !!

还可以将一个程序的输出以管道的形式作为输入传送到另一个程序,用 #| 操作符

"ls -al .." #| "grep sec" !

还有许多:

#把输出重定向得到文件  #>
"ls -al .." #> new File("output.txt") !

#追加到文件末尾而不是从头覆盖 #>>
"ls -al .." #>> new File("output.txt") !

#要把某个文件的内容作为输入,使用 #<
"grep sec" #< new File("output.txt") !

#从URL重定向输入:
"grep Scala" #< new URL("http://horstmann.com/index.html") !

p #&& q :如果p成功,则执行q
p #|| q :如果p不成功,则执行q

如果需要在不同的目录下运行进程,或者使用不同的系统变量,用Process对象的apply方法来构造ProcessBuilder,给出命令和起始目录,以及一串(名称,值)对偶来设置环境变量:

val p = Process(cmd, new File(dirName), ("LANG","en_US"))
# 然后用!操作符执行
"echo 42" #| p !

10.正则表达式

scala.util.matching.Regex

val numPattern = "[0-9]+".r

如果正则表达式包含反斜杠或者引号,最好使用”原始”字符串语法""" ... """

val wsnumwsPattern = """\s+[0-9]+\s+""".r
//一样,但是上面的容易理解
val wsnumsPattern2 = "\\s+[0-9]+\\s+".r

findAllIn方法返回所有匹配项的迭代器:

for(matchString <- numPattern.findAllIn("99 bottles, 98 bottles"))
    处理 matching

# 或者将迭代器转为数组
val matches = numPattern.findAllIn("99 bottles, 98 bottles").toArray
//Array(99,98)

找到字符串中的首个匹配项:findFirstIn,得到的结果是Option[String]

val ml = wsnumwsPattern.findFirstIn("99 bottles, 98 bottles")
//Some("98")

检查是否某个字段的开始部分能匹配:findPrefixOf

numPattern.findPrefixOf("99 bottles, 98 bottles")
//Some(99)
wsnumwsPattern.findPrefixOf("99 bottles, 98 bottles")
//None

替换首个匹配项,或全部匹配项:

numPattern.replaceFirstIn("99 bottles, 98 bottles","XX")
//"XX bottles, 98 bottles"
numPattern.replaceAllIn("99 bottles, 98 bottles","XX")
//"XX bottles, XX bottles"

11.正则表达式组

分组可以让我们更方便的获取正则表达式的子表达式,在想要提取的子表达式两侧加上圆括号:

val numitemPattern = "([0-9]+) ([a-z]+)".r

#要匹配组,把正则表达式当做提取器:
val numitemPattern(num, item) = "99 bottles"
// 将num设为99 ,item设为"bottles"

# 从多个匹配项中提取分租内容,可以这样:
for(numitemPattern(num, item) <- numitemPattern.findAllIn("99 bottles, 98 bottles")
处理 numitem

12.习题答案

1.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值