读取文件
import scala . io . Source
val source = Source . fromFile ( fileName , "UTF-8" )
// 第一个参数可以是文件名或java.io.File
// 如果没有第二个参数将会使用当前平台缺省的字符编码
val lineIterator = source . getLines // 结果是一个迭代器
// 迭代器可以转换成Array等
val contents = source . mkString // 整个文件读取成一个字符串
使用完Source后,记得需要close。
读取字符
要读取单个字符,可以直接从Source对象中进行迭代。Source类扩展自Iterator[Char]。
for ( c < - source )
如果想要查看接下来的字符,但是不对其进行处理(也就是说迭代器不移动位置),可以调用Source的buffered方法,返回一个collection.BufferedIterator[Char],然后使用这个对象的head方法查看下一个字符。
val source = Source . fromFile ( "myfile.txt" , "UTF-8" )
val iter = source . buffered
while ( iter . hasNext ) {
if ( iter . head == someChar )
deal with iter . next
else
. . .
}
source . close ( )
读取词法单元和数字
// 一个快而脏的方式
val tokens = source . mkString . sqlit ( "\\s+" ) // 根据正则读取词法单元
// 转换成数字
val numbers = for ( w < - tokens ) yield w . toDouble
val numbers = tokens . map ( _ . toDouble )
还可以使用java.util.Scanner类来读取文件中的文本和数字。
从非文件源读取
// 从URL读取,需要注意字符编码
val source1 = Source . fromURL ( "http://horstmann.com" , "UTF-8" )
val source2 = Source . fromString ( "Hello, world!" ) // 从指定的字符串读取,调试时很有用
val source3 = Source . stdin // 从标准输入读取
读取二进制文件
Scala没有提供读取二进制文件的方法,需要使用Java类库。
val file = new File ( filename )
val in = new FileInputStream ( file )
val bytes = new Array [ Byte ] ( file . length . toInt )
in . read ( bytes )
in . close ( )
写入文本文件
Scala也没有对写入文件的内建支持,依旧可以使用Java类库来实现。如:
val out = new PrintWriter ( "numbers.txt" )
for ( i < - 1 to 100 ) out . println ( i )
out . close ( )
但是在使用这里printf方法时,传递AnyVal(比如各种数字)类型给方法时,编译器会要求将其转换成AnyRef:
out . printf ( "%6d %10.2f" , quantity . asInstanceOf [ AnyRef ] ,
price . asInstanceOf [ AnyRef ] )
// 为了避免这个麻烦,可以使用String类的format方法
out . printf ( "%6d %10.2f" . format ( quantity , price ) )
访问目录
目前没有“正式的”用来访问目录中所有文件,或递归遍历所有目录的类。(本章各种没有内建没有正式是闹哪样啊!也无所谓了,用Java类库就是了。)下面探讨一下替代方案。
遍历某目录下所有子目录的函数:
import java . io . File
def subdirs ( dir : File ) : Iterator [ File ] = {
val children = dir . listFiles . filter ( _ . isDirectory )
children . toIterator ++ children . toIterator . flatMap ( subdirs _ )
}
不是很好懂,可能需要查阅API文档来帮忙。
如果使用了Java 7,可以使用java.nio.file.Files类的walkFileTree方法。该类用到了FileVisitor接口。
这里提到了Java的nio包,让我记起来的确需要去学一学了,io包大概已经真满足不了现在的需求了。
在Scala中通常更偏好用函数对象来指定工作内容,而不是接口(但在本例中接口可以有更细粒度的控制)。
以下隐式转换让函数可以与接口相匹配:
import java . nio . file . _
implicit def makeFileVisitor ( f : ( Paht ) = > Unit ) = new SimpleFileVisitor [ Path ] {
override def visitFile ( p : Path , attrs : attribute . BasicFileAttributes ) = {
f ( p )
FileVisitResult . CONTINUE
}
}
// 调用打印出所有的子目录
Files . walkFileTree ( dir . toPaht , ( f : Path ) = > println ( f ) )
这个真心有点超出太多了,放着先吧,等学了相关的知识再回来看。
序列化
使用序列化来将对象传输,或者是临时存储,序列化并不适合长期存储,会因为类的更新而出现问题。
在Scala中的序列化类:
@SerialVersionUID ( 42L ) class Person extends Serializable
如果可以接受缺省的UID,可以不要@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 ]
in . close ( )
进程控制(A2)
Scala设计目标之一是能在简单的脚本话任务和大型程序之间保持良好的伸缩性。
scala.sys.process包提供了用于与shell程序交互的工具。这样一来,就可以使用Scala编写shell脚本。(虽然我用了Linux不少时间了,但我还是不会bash编程啊!怎么会有这么多东西需要学啊!~来不及的感觉…)
简单例子:
import sys . process . _
"ls -al .."!
ls -al ..命令将会被执行,然后将结果在标准输出中显示。
sys.process包内有从字符串到ProcessBuilder对象的隐式转换。!操作符执行的就是这个ProcessBuilder对象。返回的结果是被执行程序的返回值:成功执行是0;否则是显示错误的非0值。
如果使用!!操作符,输出会以字符串的形式返回:
val result = "ls -al .." ! !
还可以使用管道(不了解什么是管道的,请先学习一下Linux基础),使用#!操作符。
// 管道,使用#|
"ls -al .." # | "grep sec" !
// 重定向到文件,使用#>
"ls -al .." # > new File ( "output.txt" ) !
// 追加到文件,使用#>>
"ls -sl .." # >> new File ( "output.txt" ) !
// 将文件内容作为输入,使用#<
"grep sec" # < new File ( "output.txt" ) ! // 也可以使用URL
另外还可以使用#||和#&&来控制进程。不过这种功能还是直接使用Scala来实现更好。
可以自己指定执行进程的目录和环境变量;设置环境变量用的是一系列的对偶。
val p = Process ( cmd , new File ( dirName ) , ( "LANG" , "en_US" ) )
"echo 42" # | p ! // 执行
个人认为这个功能还是很强大的,可以使用现有的命令来做到大量的事情。不过是否会带来安全上的影响,以及可能会使程序变得与平台有关,还是要慎重使用。
正则表达式
与正则表达式相关的类是scala.util.matching.Regex类。要构造一个Regex对象,使用String类的r方法即可。如果正则表达式中包含反斜杠或引号之类的需要转义的字符,那么最好是使用原始(raw)字符串,以三个”号包围。
val numPattern = "[0-9]+" . r
val wsnumwsPattern = "" "\s+[0-9]+\s+" "" . r
// findAllIn方法返回遍历所有匹配项的迭代器
for ( matchString < - numPattern . findAllIn ( "99 bottles, 98 bottles" ) )
// 找到首个匹配项
val m1 = wsnumwsPattern . findFirstIn ( "99 bottles, 98 bottles" )
// 返回Some(" 98 ")
// findFirstIn方法返回Option[String]
用findPrefixOf方法检查某个字符串的开始部分是否能够匹配。
可以用相应的replace方法来替换掉匹配的部分。
正则表达式组
分组使得获取正则表达式的子表达式更加方便。在想要提取的子表达式两侧加上圆括号:
val numitemPattern = "([0-9]+) ([a-z]+" . r
匹配组将正则表达式对象当做“提取器”来使用:
val numitemPattern ( num , item ) = "99 bottles"
// num被赋值为"99",item被赋值为"bottles"
相关资源:Scala中正则表达式以及与模式匹配结合(多种方式)_scala正则表达式...