原文发表于:http://nerd-is.in/2013-08/scala-learning-files-and-regular-expressions
读取文件
|
importscala.io.Source
valsource=Source.fromFile(fileName,"UTF-8")
// 第一个参数可以是文件名或java.io.File
// 如果没有第二个参数将会使用当前平台缺省的字符编码
vallineIterator=source.getLines // 结果是一个迭代器
// 迭代器可以转换成Array等
valcontents=source.mkString // 整个文件读取成一个字符串
|
使用完Source后,记得需要close。
读取字符
要读取单个字符,可以直接从Source对象中进行迭代。Source类扩展自Iterator[Char]。
如果想要查看接下来的字符,但是不对其进行处理(也就是说迭代器不移动位置),可以调用Source的buffered方法,返回一个collection.BufferedIterator[Char],然后使用这个对象的head方法查看下一个字符。
|
valsource=Source.fromFile("myfile.txt","UTF-8")
valiter=source.buffered
while(iter.hasNext){
if(iter.head==someChar)
deal withiter.next
else
...
}
source.close()
|
读取词法单元和数字
|
// 一个快而脏的方式
valtokens=source.mkString.sqlit("\\s+") // 根据正则读取词法单元
// 转换成数字
valnumbers=for(w<-tokens)yieldw.toDouble
valnumbers=tokens.map(_.toDouble)
|
还可以使用java.util.Scanner类来读取文件中的文本和数字。
从非文件源读取
|
// 从URL读取,需要注意字符编码
valsource1=Source.fromURL("http://horstmann.com","UTF-8")
valsource2=Source.fromString("Hello, world!") // 从指定的字符串读取,调试时很有用
valsource3=Source.stdin // 从标准输入读取
|
读取二进制文件
Scala没有提供读取二进制文件的方法,需要使用Java类库。
|
valfile=newFile(filename)
valin=newFileInputStream(file)
valbytes=newArray[Byte](file.length.toInt)
in.read(bytes)
in.close()
|
写入文本文件
Scala也没有对写入文件的内建支持,依旧可以使用Java类库来实现。如:
|
valout=newPrintWriter("numbers.txt")
for(i<-1to100)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类库就是了。)下面探讨一下替代方案。
遍历某目录下所有子目录的函数:
|
importjava.io.File
defsubdirs(dir:File):Iterator[File]={
valchildren=dir.listFiles.filter(_.isDirectory)
children.toIterator++children.toIterator.flatMap(subdirs_)
}
|
不是很好懂,可能需要查阅API文档来帮忙。
如果使用了Java 7,可以使用java.nio.file.Files类的walkFileTree方法。该类用到了FileVisitor接口。
这里提到了Java的nio包,让我记起来的确需要去学一学了,io包大概已经真满足不了现在的需求了。
在Scala中通常更偏好用函数对象来指定工作内容,而不是接口(但在本例中接口可以有更细粒度的控制)。
以下隐式转换让函数可以与接口相匹配:
|
importjava.nio.file._
implicitdefmakeFileVisitor(f:(Paht)=>Unit)=newSimpleFileVisitor[Path]{
overridedefvisitFile(p:Path,attrs:attribute.BasicFileAttributes)={
f(p)
FileVisitResult.CONTINUE
}
}
// 调用打印出所有的子目录
Files.walkFileTree(dir.toPaht,(f:Path)=>println(f))
|
这个真心有点超出太多了,放着先吧,等学了相关的知识再回来看。
序列化
使用序列化来将对象传输,或者是临时存储,序列化并不适合长期存储,会因为类的更新而出现问题。
在Scala中的序列化类:
|
@SerialVersionUID(42L)classPerson extendsSerializable
|
如果可以接受缺省的UID,可以不要@SerialVersionUID注解。
序列化和反序列化:
|
valfred=newPerson(...)
importjava.io._
valout=newObjectOutputStream(newFileOutputStream("/tmp/test.obj"))
out.writeObject(fred)
out.close()
valin=newObjectInputStream(newFileInputStream("/tmp/test.obj"))
valsavedFred=in.readObject().asInstanceOf[Person]
in.close()
|
进程控制(A2)
Scala设计目标之一是能在简单的脚本话任务和大型程序之间保持良好的伸缩性。
scala.sys.process包提供了用于与shell程序交互的工具。这样一来,就可以使用Scala编写shell脚本。(虽然我用了Linux不少时间了,但我还是不会bash编程啊!怎么会有这么多东西需要学啊!~来不及的感觉…)
简单例子:
|
importsys.process._
"ls -al .."!
|
ls -al ..命令将会被执行,然后将结果在标准输出中显示。
sys.process包内有从字符串到ProcessBuilder对象的隐式转换。!操作符执行的就是这个ProcessBuilder对象。返回的结果是被执行程序的返回值:成功执行是0;否则是显示错误的非0值。
如果使用!!操作符,输出会以字符串的形式返回:
还可以使用管道(不了解什么是管道的,请先学习一下Linux基础),使用#!操作符。
|
// 管道,使用#|
"ls -al .."#|"grep sec"!
// 重定向到文件,使用#>
"ls -al .."#>newFile("output.txt")!
// 追加到文件,使用#>>
"ls -sl .."#>>newFile("output.txt")!
// 将文件内容作为输入,使用#<
"grep sec"#<newFile("output.txt")! // 也可以使用URL
|
另外还可以使用#||和#&&来控制进程。不过这种功能还是直接使用Scala来实现更好。
可以自己指定执行进程的目录和环境变量;设置环境变量用的是一系列的对偶。
|
valp=Process(cmd,newFile(dirName),("LANG","en_US"))
"echo 42"#|p! // 执行
|
个人认为这个功能还是很强大的,可以使用现有的命令来做到大量的事情。不过是否会带来安全上的影响,以及可能会使程序变得与平台有关,还是要慎重使用。
正则表达式
与正则表达式相关的类是scala.util.matching.Regex类。要构造一个Regex对象,使用String类的r方法即可。如果正则表达式中包含反斜杠或引号之类的需要转义的字符,那么最好是使用原始(raw)字符串,以三个”号包围。
|
valnumPattern="[0-9]+".r
valwsnumwsPattern="""\s+[0-9]+\s+""".r
|
|
// findAllIn方法返回遍历所有匹配项的迭代器
for(matchString<-numPattern.findAllIn("99 bottles, 98 bottles"))
// 找到首个匹配项
valm1=wsnumwsPattern.findFirstIn("99 bottles, 98 bottles")
// 返回Some(" 98 ")
// findFirstIn方法返回Option[String]
|
用findPrefixOf方法检查某个字符串的开始部分是否能够匹配。
可以用相应的replace方法来替换掉匹配的部分。
正则表达式组
分组使得获取正则表达式的子表达式更加方便。在想要提取的子表达式两侧加上圆括号:
|
valnumitemPattern="([0-9]+) ([a-z]+".r
|
匹配组将正则表达式对象当做“提取器”来使用:
|
valnumitemPattern(num,item)="99 bottles"
// num被赋值为"99",item被赋值为"bottles"
|
相关推荐
有一个行解析器配置机制,允许程序员改变用于识别字符串和分隔符的正则表达式,也改变引号字符。 除了解析之外, TableParser还提供了一种以分层形式(例如对于 XML 或 HTML)呈现表格的机制。 使用表格的行以及诸如...
例如,它可以支持更复杂的模式,如正则表达式匹配,或者在匹配过程中进行更复杂的条件判断。 从"matchete.zip"的文件名称来看,我们可以推测这是Matchete项目的源码压缩包,"master"分支通常是GitHub上项目的主分支...
7. **路由匹配**:Wiro可能提供灵活的路由匹配机制,如正则表达式、参数化路径等,使开发者可以创建复杂的路由规则。 8. **RPC支持**:考虑到标签中的"rpc",Wiro可能还具备支持远程过程调用的功能,允许服务间通信...
- 正则表达式用于匹配字符串中的模式。 - **反射技术** - 反射允许程序在运行时获取类的信息。 - 可以创建对象、调用方法等。 #### 三、Linux系统管理 - **Linux介绍** - Linux是一个免费开源的操作系统。 - ...
- **正则表达式**:讲解Java中的正则表达式基础,包括模式匹配、分组、替换等操作。 - **Java反射技术**:探讨Java反射机制的基本原理及其应用场景,了解Class类、Field类、Method类和Constructor类的使用。 - **...