9.文件和正则表达式
9.1 读取文件的行
a:例如:
import scala.io.Source
val source = Source.fromFile("myfile.txt", "UTF-8")
val lineIterator = source.getLines
b:对每行做迭代
for (l <- lineIterator) process l
c:将所有的行转化成一个数组
val lines = source.getLines.toArray
d:将整个文件读成一个字符串
val contents = source.mkString
e:记得关闭文件
source.close
9.2 读取字符
a:使用source 对象
for (c <- source) process c
b:在还没有读取字符的时候,就知道下一个字符,例如:
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 类型
val tokens = source.mkString.split("\\s+")
val numbers = for (w <- tokens) yield w.toDouble
或者 val numbers = tokens.map(_.toDouble)、
d:从URL 读取 或者标准输入读取文件
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 的类库读取二进制文件 ,
例如:
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 方法
例如:
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:序列化和反序列化对象方法:
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 脚本命令
例如:
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 对象
例如:
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:如果正则表达式中包含了 反斜杆\,双引号",则需要使用 """...""" 这种形式
例如:
//// A bit easier to read than "\\s+[0-9]+\\s+".r
val wsnumwsPattern = """\s+[0-9]+\s+""".r
d:正则表达式使用 findAllIn 方法返回一个所用匹配的Iterator
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:从多个匹配中获取组,使用如下语句:
for (numitemPattern(num, item) <- numitemPattern.findAllIn("99 bottles, 98 bottles"))
process num and item
10.function 以及closures
10.1 本地函数local function
a:将private 函数定义 嵌套在 主调用方法中,对外界屏蔽了实现细节
例如:
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) 调用该函数
例如:
scala> var increase = (x: Int) => x + 1
increase: (Int) => Int = <function1>
scala> increase(10)
res0: Int = 11
e: 在function literal 中,函数体有多个语句,则需要用{} 构造语句块,
其中每个语句都单独为一行
例如:
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:去掉():如果去掉了参数类型,则可以去掉()
例如:
scala> someNumbers.filter(x => x > 0)
res6: List[Int] = List(5, 10)
c: 使用_ 下划线做单个参数的占位符号
例如:
someNumbers.filter(_ > 0)
val f = (_: Int) + (_: Int)
10.4 Partially applied functions 部分应用的函数
a:部分应用函数 表达式 使用方法
functionName _
例如:
print _
sum _
b:例如:
scala> val a = sum _
a: (Int, Int, Int) => Int = <function3>
生成一个a引用指向新的函数值,其实上述方法调用相当与
scala> a(1, 2, 3)
res11: Int = 6
scala> a.apply(1, 2, 3)
res12: Int = 6
c:functionName _ 可以实现 包裹方法和功能function嵌套
d:对原函数的所有参数 只是提供部分参数 并不提供全部的参数 也是应用的一种情况
例如:
scala> val b = sum(1, _: Int, 3)
b: (Int) => Int = <function1>
scala编译器会为产生一个其中apply方法只是携带一个参数的 函数类一个
下面就是一些调用例子:
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:
scala> var more = 1
more: Int = 1
scala>[color=red] val addMore = (x: Int) => x + more[/color]
addMore: (Int) => Int = <function1>
scala> addMore(10)
res17: Int = 11
def makeIncreaser(more: Int) = (x: Int) => x + more
c:closures 能够捕获变量的本身,而不是变量指向的引用值。因此当外面的变量值变化时,则closures 也能够捕获到这个变化
d:同理 在closures 中改变的变量值 也会被外面的变量给捕获到,例如:
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:可重复的参数
参数类型后面加上字符*,就表示可重复的参数,例如:
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:带名字的参数以及具有默认值的参数
带名字的参数,
参数名=参数值,该种类型允许传递的参数可以不一参数定义的顺序进行传递
例如:
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
具有默认值的参数: 则允许不传递参数,而使用默认值
例如:
def printTime(out: java.io.PrintStream = Console.out) =
out.println("time = "+ System.currentTimeMillis())
10.7 尾部遍历以及优化