9.文件和正则表达式
9.1 读取文件的行
a:例如:
Java代码
- import scala.io.Source
- val source = Source.fromFile("myfile.txt", "UTF-8")
- val lineIterator = source.getLines
b:对每行做迭代
Java代码
- for (l <- lineIterator) process l
c:将所有的行转化成一个数组
Java代码
- val lines = source.getLines.toArray
d:将整个文件读成一个字符串
Java代码
- val contents = source.mkString
e:记得关闭文件
source.close
9.2 读取字符
a:使用source 对象
Java代码
- for (c <- source) process c
b:在还没有读取字符的时候,就知道下一个字符,例如:
Java代码
- val source = Source.fromFile("myfile.txt", "UTF-8")
- val iter = source.buffered
- while (iter.hasNext) {
- if (iter.head is nice)
- process iter.next
- else
- ...
- }
- source.close()
c:读取以空白分割的字符,将字符专程number 类型
Java代码
- val tokens = source.mkString.split("\\s+")
- val numbers = for (w <- tokens) yield w.toDouble
- 或者 val numbers = tokens.map(_.toDouble)、
d:从URL 读取 或者标准输入读取文件
Java代码
- val source1 = Source.fromURL("http://horstmann.com", "UTF-8")
- // Reads from the given string—useful for debugging
- val source2 = Source.fromString("Hello, World!")
- // Reads from standard input
val source3 = Source.stdin
e:读取二进制文件
使用java 的类库读取二进制文件 ,
例如:
Java代码
- val file = new File(filename)
- val in = new FileInputStream(file)
- val bytes = new Array[Byte](file.length.toInt)
- in.read(bytes)
- in.close()
f:写文件
使用java 的类库写文件
g:遍历路径
g1: 可以使用JDK1.7 nio.Files 下的 walkFileTree 方法
例如:
Java代码
- 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))
h:序列化
h1:序列化和反序列化对象方法:
Java代码
- 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]
h2:scala 定义序列化对象
@SerialVersionUID(42L) class Person extends Serializable
其中@SerialVersionUID(42L) 可以省略
h3:在scala 中collection 类都是可以序列化的e
9.9 处理控制
a:使用process 包执行shell 脚本命令
例如:
Java代码
- import sys.process._
- "ls -al .." !
- ls-all .. 命令被执行 并且 显示了父目录下的所有文件 ,同时将结果输出到控制台
- sys.process 将String 隐式转换成ProcessBuider 对象
- ! :执行ProcessBuilder 对象。如果执行成功返回0,不成功,则返回非0
- !!: 执行结果作为String 返回 ,例如: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 执行q 如果p 执行成功
- #||:p #|| q 执行q,如果p 没有执行成功
b:构造Process 对象执行命令,使用 ! 命令执行Process 对象
例如:
Java代码
- val p = Process(cmd, new File(dirName), ("LANG", "en_US"))
- "echo 42" #| p !
9.10 正则表达式
a: 类名: scala.util.matching.Regex
b:构造正则表达式:使用String 的 .r 方法,例如
val numPattern = "[0-9]+".r
c:如果正则表达式中包含了 反斜杆\,双引号",则需要使用 """...""" 这种形式
例如:
Java代码
- //// A bit easier to read than "\\s+[0-9]+\\s+".r
- al wsnumwsPattern = """\s+[0-9]+\s+""".r
d:正则表达式使用 findAllIn 方法返回一个所用匹配的Iterator
Java代码
- for (matchString <- numPattern.findAllIn("99 bottles, 98 bottles"))
- process matchString
- 或者转化成Array
- val matches = numPattern.findAllIn("99 bottles, 98 bottles").toArray
findAllIn: 找到全部匹配的字符串
findFirstIn:找到第一个匹配的字符串
findPrefixOf:找到一个字符串的开始是否匹配的对象
replaceFirstIn:替换第一个匹配的字符串
replaceAllIn: 替换所有匹配的字符串
9.11 正则表达式组
a:用() 包含一个子组,例如 val numitemPattern = "([0-9]+) ([a-z]+)".r
b:从多个匹配中获取组,使用如下语句:
Java代码
- for (numitemPattern(num, item) <- numitemPattern.findAllIn("99 bottles, 98 bottles"))
- rocess num and item
10.function 以及closures
10.1 本地函数local function
a:将private 函数定义 嵌套在 主调用方法中,对外界屏蔽了实现细节
例如:
Java代码
- def processFile(filename: String, width: Int) {
- def processLine(filename: String,
- width: Int, line: String) {
- if (line.length > width)
- println(filename +": "+ line)
- }
- val source = Source.fromFile(filename)
- for (line <- source.getLines()) {
- processLine(filename, width, line)
- }
b:本地函数可以访问 外面包围函数的参数
10.2 first-class function
a:function literal 语法
(函数参数1,函数参数2)=>{函数体}
例如:
(x:Int,y:Int)=>x+y
b:function literal 被编译成一个类,在运行时期被实例化,称为function value
c:function literal 和function value 之间的区别是:
literal 存在于源码中,而value 存在与运行时期的object
d:function value 由于在运行时期是一个object,因此可以作为变量赋值给对象,
之后可以使用函数调用的方法 functionanme(param) 调用该函数
例如:
Java代码
- scala> var increase = (x: Int) => x + 1
- increase: (Int) => Int = <function1>
- scala> increase(10)
- res0: Int = 11
e: 在function literal 中,函数体有多个语句,则需要用{} 构造语句块,
其中每个语句都单独为一行
例如:
Java代码
- scala> increase = (x: Int) => {
- println("We")
- println("are")
- println("here!")
- x + 1
- }
- increase: (Int) => Int = <function1>
- scala> increase(10)
- We
- are
- here!
- res2: Int = 11
10.3 function literal 的简写形式
a:去掉参数类型 :如果参数类型能够根据调用者推断出来,则可以去掉参数的类型
b:去掉():如果去掉了参数类型,则可以去掉()
例如:
Java代码
- scala> someNumbers.filter(x => x > 0)
- es6: List[Int] = List(5, 10)
c: 使用_ 下划线做单个参数的占位符号
例如:
Java代码
- someNumbers.filter(_ > 0)
- val f = (_: Int) + (_: Int)
10.4 Partially applied functions 部分应用的函数
a:部分应用函数 表达式 使用方法
functionName _
例如:
Java代码
b:例如:
Java代码
- scala> val a = sum _
- a: (Int, Int, Int) => Int = <function3>
生成一个a引用指向新的函数值,其实上述方法调用相当与
Java代码
- scala> a(1, 2, 3)
- res11: Int = 6
- scala> a.apply(1, 2, 3)
- res12: Int = 6
c:functionName _ 可以实现 包裹方法和功能function嵌套
d:对原函数的所有参数 只是提供部分参数 并不提供全部的参数 也是应用的一种情况
例如:
Java代码
- scala> val b = sum(1, _: Int, 3)
- b: (Int) => Int = <function1>
- scala编译器会为产生一个其中apply方法只是携带一个参数的 函数类一个
下面就是一些调用例子:
Java代码
- scala> b(2)
- res13: Int = 6
- scala> b(5)
- res14: Int = 9
d: 如果调用点指定要求的类型是 函数类型时,如果要为 该参数 传入 functionName _ ,其中_ 可以省略
10.5 定义closures
a:定义closures:
在运行时根据function literal 创造的function value ,称为closures
b:
Java代码
- scala> var more = 1
- more: Int = 1
- scala> val addMore = (x: Int) => x + more
- addMore: (Int) => Int = <function1>
- scala> addMore(10)
- res17: Int = 11
def makeIncreaser(more: Int) = (x: Int) => x + more
c:closures 能够捕获变量的本身,而不是变量指向的引用值。因此当外面的变量值变化时,则closures 也能够捕获到这个变化
d:同理 在closures 中改变的变量值 也会被外面的变量给捕获到,例如:
Java代码
- scala> val someNumbers = List(-11, -10, -5, 0, 5, 10)
- someNumbers: List[Int] = List(-11, -10, -5, 0, 5, 10)
- scala> var sum = 0
- sum: Int = 0
- scala> someNumbers.foreach(sum +=
- _)
- scala> sum
- res20: Int = -11
10.6 函数调用的特殊形式
a:可重复的参数
参数类型后面加上字符*,就表示可重复的参数,例如:
Java代码
- def echo(args: String*) =
- for (arg <- args) println(arg)
- scala> echo()
- scala> echo("one")
- one
- scala> echo("hello", "world!")
- hello
- world!
技术上 String* 相当与Array[String] ,单数如果给 echo 传递 Array 对象,则会报类型的编译错误,因此对于Array 可以修改为:
echo(arr: _*)
b:带名字的参数以及具有默认值的参数
带名字的参数,
参数名=参数值,该种类型允许传递的参数可以不一参数定义的顺序进行传递
例如:
Java代码
- scala> def speed(distance: Float, time: Float): Float =
- distance / time
- speed: (distance: Float,time: Float)Float
- scala> speed(100, 10)
- res28: Float = 10.0
- scala> speed(distance = 100, time = 10)
- res29: Float = 10.0
- scala> speed(time = 10, distance = 100)
- res30: Float = 10.0
具有默认值的参数: 则允许不传递参数,而使用默认值
例如:
Java代码
- def printTime(out: java.io.PrintStream = Console.out) =
- out.println("time = "+ System.currentTimeMillis())