Scala学习(九)---文件和正则表达式
文件和正则表达式 |
摘要:
在本篇中,你将学习如何执行常用的文件处理任务,比如从文件中读取所有行或单词,或者读取包含数字的文件等。本篇的要点包括:
1. Source.fromFile(...).getLines.toArray输出文件的所有行
2. Source.fromFile(...).mkString以字符串形式输出文件内容
3. 将字符串转换为数字,可以用tolnt或toDouble方法
4. 使用java的PrintWriter来写入文本文件
5. "正则",r是一个Regex对象
6. 如果你的正则表达式包含反斜杠或引号的话,用"""…"""
7. 如果正则模式包含分组,你可以用如下语法来提取它们的内容for (regex(变量1,变量n) <- 字符串)
读取行 |
要读取文件中的所有行,可以调用scala.io.Source对象的getLines方法:
import scala.io.Source
val source=Source.fromFile ("myfile.txt", "UTF-8")
第一个参数可以是字符串或者是java.io.File,如果你知道文件使用的是当前平台缺省的字符编码,则可以略去第二个字符编码参数
val linelterator = source.getLines
结果是一个迭代器。你可以用它来逐条处理这些行:
for (l <- linelterator ) 处理l
或者你也可以对迭代器应用toArray或toBuffer方法,将这些行放到数组或数组缓冲当中:
val lines=source.getLines.toArray
有时候,你只想把整个文件读取成一个字符串。那更简单了:
val contents=source.mkString
需要注意的是,在用完Source的对象后,记得调用close
读取字符 |
要从文件中读取单个字符,你可以直接把Source对象当做迭代器,因为Source类扩展自Iterator[Char]:
for (c <- source) 处理c
如果你想查看某个字符但又不处理掉它的话,调用source对象的buffered方法。这样你就可以用head方法查看下一个字符,但同时并不把它当做是已处理的字符
val source = Source.fromFile ( "myfile.txt ", "UTF-8 ")
val iter=source.buffered
while ( iter.hasNext ) {
if ( iter.head是符合预期的 )
处理iter.next
else
…….
}
Source .close()
或者,如果你的文件不是很大,你也可以把它读取成一个字符串进行处理:
val contents = source.mkString
读取词法单元和数字 |
这里有一个快而脏的方式来读取源文件中所有以空格隔开的词法单元:
val tokens = source.mkString.split("\\S+")
而要把字符串转换成数字,可以用tolnt或toDouble方法。举例来说,如果你有一个包含了浮点数的文件,则可以将它们统统读取到数组中:
val numbers=for (w <- tokens) yield w.toDouble
或者
val numbers = tokens.map(_.toDouble)
需要注意的是,我们总是可以使用java.util.Scanner类来处理同时包含文本和数字的文件。与此同时,你也可以从控制台读取数字:
print (" How old are you" ) // 缺省情况下系统会自动引入Console,并不需要对print和readlnt使用限定词
val age=readlnt()
注意:这些方法假定下一行输入包含单个数字,且前后都没有空格。否则会报NumberFormatException
从URL或其他源读取 |
Source对象有读取非文件源的方法:
val source1 = Source.fromURL("http://horstamnn.com", "UTF-8")
val source2 = Source.fromString( "Hello, World! " ) // 从给定的字符串读取,这对调试很有用
val source3 = Source.stdin //从标准输入读取
当你从URL读取时,你需要事先知道字符集,可能是通过HTTP头获取。更多信息参见www.w3.org/lnternational/O-charset
读取二进制文件 |
Scala并没有提供读取二进制文件的方法。你需要使用Java类库。以下是如何将文件读取成字节数组:
val file = new File (filename)
val in = new FileInputStream(file)
val bytes = new Array[Byte](file.length.tolnt)
in.read (bytes)
in.close()
写入文本文件 |
Scala没有内建的对写入文件的支持。要写入文本文件,可使用java.io.PrintWriter,例如:
val out = new PrintWriter("numbers.txt")
for ( i <- 1 to 100 ) out.println(i)
out.close()
所有的逻辑都像我们预期的那样,除了printf方法外。当你传递数字给printf时,编译器会抱怨说你需要将它转换成AnyRef:
out.printf ( "%6d %10.2f",quantity.aslnstanceOf [AnyRef], price.aslnstanceOf[AnyRef] )
为了避免这个麻烦,你也可以用string类的format方法:
out.print( "%6d %10.2f". format (quantity, price))
需要注意的是:Console类的printf没有这个问题,你可以用来输出消息到控制台。
printf("%6d %10.2f",quantity, price)
访问目录 |
自定义处理
目前Scala并没有"正式的"用来访问某个目录中的所有文件,或者递归地遍历所有目录的类。下面,我们将探讨一些替代方案。编写产出遍历某目录下所有子目录的函数并不复杂:
import java.io.File
def subdirs (dir: File):Iterator[File] = {
val children = dir.listFiles.filter(_.isDirectory)
children.tolterator ++ children.toIterator.flatMap( subdirs _ )
}
利用这个函数,你可以像这样访问所有的子目录:
for(d <- subdirs (dir))处理d
或者,如果你用的是Java 7,你也可以使用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) )
当然,如果你不仅仅是想要打印出这些文件,则也可以在传AwalkFileTree方法的函数中指定其他要执行的动作
序列化 |
在Java中,我们用序列化来将对象传输到其他虚拟机,或临时存储。对于长期存储而言,序列化可能会比较笨拙,因为随着类的演进更新,处理不同版本间的对象是很烦琐的一件事。以下是如何在Java和Scala中声明一个可被序列化的类。
Java
public class Person implements java.io.Serializable {
private static final long serialVersionUID=42L;
}
Scala
@SerialVersionUID(42L) class Person extends Serializable
Serializable特质定义在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 ObjectlnputStream ( new FilelnputStream("/tmp/test.obj")
val savedFred=in.readObject() .aslnstanceOf[Person]
Scala集合类都是可序列化的,因此你可以把它们用做你的可序列化类的成员:
class Person extends Serializable{
private val friends = new ArrayBuffer[Person] // ArrayBuffer是可序列化的
}
进程控制 |
Scala脚本
按照传统习惯,程序员使用shell脚本来执行日常处理任务,比如把文件从一处移动到另一处,或者将一组文件拼接在一起。shell语言使得我们可以很容易地指定所需要的文件子集,以及将某个程序的输出以管道方式作为另一个程序的输入。话虽如此,从编程语言的角度看,大多数shell语言并不是那么完美。
Scala的设计目标之一就是能在简单的脚本化任务和大型程序之间保持良好的伸缩性。scala.sys.process包提供了用于与shell程序交互的工具。你可以用Scala编写shell脚本,利用Scala提供的所有威力。如下是一个简单的示例:
import sys.process._
"ls -al .." !
这样做的结果是,Is -al ..命令被执行,显示上层目录的所有文件。执行结果被打印到标准输出。sys.process包包含了一个从字符串到ProcessBuilder对象的隐式转换。!操作符执行的就是这个ProcessBuilder对象。!操作符返回的结果是被执行程序的返回值:程序成功执行的话就是0,否则就是显示错误的非0值。
操作符和管道
如果你使用! !操作符而不是!操作符的话,输出会以字符串的形式返回:
val result = "ls -al .." ! !
你还可以将一个程序的输出以管道形式作为输入传送到另一个程序,用}}I操作符:
"ls -al .." #| "grep sec" !
正如你看到的,进程类库使用的是底层操作系统的命令。在本例中,我用的是bash命令,因为bash在Linux、Mac OS X和Windows中都能找到
常用操作符
要把输出重定向到文件,使用撑#>操作符:
"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。由上可知,进程库使用人们熟悉的shell操作符I > >> < && ||,只不过给它们加上了#前缀,因此它们的优先级是相同的。
不同目录与环境变量运行进程
如果你需要在不同的目录下运行进程,或者使用不同的环境变量,用Process对象的apply方法来构造ProcessBuilder,给出命令和起始目录,以及一串(名称,值)对偶来设置环境变量:
val p=Procass (cmd, new File (dirName), ("_LANG","nen US"))
然后用!操作符执行它:
"ech0 42" #I p !
正则表达式 |
当你在处理输入的时候,你经常会想要用正则表达式来分析它。scala.util.matching.Regex类让这件事情变得简单。要构造一个Regex对象,用String类的r方法即可:
val numPattern="[0-9]+ ".r
如果正则表达式包含反斜杠或引号的话,那么最好使用"原始"字符串语法。例如:
val wsnumwsPattern = """\s+[0-9]+\s+""".r // 和"\\s+[0-9]+\\s+".r相比要更易读一些
findAllln方法返回遍历所有匹配项的迭代器。你可以在for循环中使用它:
for ( matchString <- numPattern.findAllln( "99 bottles, 98 bottles"))
处理matchString
或者将迭代器转成数组:
val matches = numPattern.findAllln("99 bottles, 98 bottles").toArray // Array(99, 98)
要找到字符串中的首个匹配项,可使用findFirstln。你得到的结果是一个Option[String]
val ml = wsnumwsPattern.findFirstln("99 bottles, 98 bottles") //Some("98")
要检查是否某个字符串的开始部分能匹配,可用findPrefixOf:
numPattern.findPreflxOf("99 bottlesf 98 bottles") //Some(99)
wSnumwsPattern.findPrefixOf("99 bottles, 98 bottles") // None
你可以替换首个匹配项,或全部匹配项:
numPattern.replaceFirstln("99 bottles, 98 bottles", "XX") // "XX bottles, 98 bottles"
numPattern. replaceAllIn("99 bottles, 98 bottles", "XX") // "XX bottles, XX bottles"
正则表达式组 |
分组可以让我们方便地获取正则表达式的子表达式。在你想要提取的子表达式两侧加上圆括号,例如:
val numitemPattern = " ([0-9]+) ([a-z]+) ".r
要匹配组,可以把正则表达式对象当做"提取器"使用,就像这样:
val numitemPattern (num, item) = "99 bottles" // 将num设为"99",item设为"bottles"
如果你想要从多个匹配项中提取分组内容,可以像这样使用for语句:
for (numitemPattern (num,item) <- numitemPattern.findAllln("99 bottles, 98 bottles"))
处理num和item
如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】。
如果,您希望更容易地发现我的新博客,不妨点击一下左下角的【关注我】。
如果,您对我的博客所讲述的内容有兴趣,请继续关注我的后续博客,我是【Sunddenly】。本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利
Scala学习(九)---文件和正则表达式的更多相关文章
- Scala学习九——文件和正则表达式
一.本章要点 Source.fromFile(...).getLines.toArray输出文件的所有行; Source.fromFile(...).mkString以字符串形式输出文件内容; 将字符 ...
- Scala学习(九)练习
文件正则表达式&练习 1. 编写一小段Scala代码,将某个文件中的行倒转顺序,将最后一行作为第一行,依此类推 程序代码: import scala.io.Source import java ...
- Scala学习笔记--文件IO
补充: http://blog.csdn.net/lyrebing/article/details/20369445 http://developer.51cto.com/art/200907/134 ...
- 初步学习pg_control文件之九
接前文,初步学习pg_control文件之八 来看这个: pg_time_t time; /* time stamp of last pg_control update */ 当初初始化的时候,是这样 ...
- 机器学习(三)--- scala学习笔记
Scala是一门多范式的编程语言,一种类似Java的编程语言,设计初衷是实现可伸缩的语言.并集成面向对象编程和函数式编程的各种特性. Spark是UC Berkeley AMP lab所开源的类Had ...
- 【大数据】Scala学习笔记
第 1 章 scala的概述1 1.1 学习sdala的原因 1 1.2 Scala语言诞生小故事 1 1.3 Scala 和 Java 以及 jvm 的关系分析图 2 1.4 Scala语言的特点 ...
- 【Scala】Scala学习资料
Scala学习资料 java 树形 分类器_百度搜索 决策树分类器-Java实现 - CSDN博客 KNN分类器-Java实现 - CSDN博客 学习:java设计模式—分类 - 飞翔荷兰人 - 博客 ...
- 初步学习pg_control文件之十
接前文 初步学习pg_control文件之九 看下面这个 XLogRecPtr checkPoint; /* last check point record ptr */ 看看这个pointer究竟保 ...
- Scala学习随笔——Scala起步
实验楼学习Scala语言的笔记,课程网址为https://www.shiyanlou.com/courses/490 一.Scala简介 Scala 是一门多范式的编程语言,类似于 Java .设计初 ...
随机推荐
- WPF:完美自定义MeaagseBox 2.0
很久前做个一个MessageBox,原文链接:http://www.cnblogs.com/DoNetCoder/p/3843658.html. 不过对比MessageBox还有一些瑕疵.这些天有时间 ...
- 解决VS2015单元测试“未能设置用于运行测试的执行上下文”问题
VS的单元测试在进行测试时并不像普通Exe会为你提示xx文件未找到,而是类似下面这样: 测试名称: 部署文件到Linux测试全名: unittest::SmartDispatch::部署文件到Linu ...
- Jmeter-测试计划,线程组,取样器,逻辑控制器,断言和监听器
一 测试计划: 是使用jmeter测试的起点,是其他测试元件的容器,一个完整的测试计划包括多个线程组,逻辑控制器,取样器,监听器,配置元件 用户定义的变量: 测试计划上可以添加用户定义的变量.一般添加 ...
- Scala之Calendar,SimpleDateFormat简单用法
package com.dingxin.entrance import java.text.SimpleDateFormat import java.util.{Calendar, Date} /** ...
- 翻译:MySQL "Got an Error Reading Communication Packet" Errors
前言: 本文是对Muhammad Irfan的这篇博客MySQL "Got an Error Reading Communication Packet" Errors的翻译,如有翻 ...
- Linux如何查看YUM的安装目录
Linux下如何查看使用YUM安装过的包的安装路径呢? 在搞清楚这个问题前,我们先来了解一下YUM. YUM(全称为 Yellow dog Updater, Modified)是一个在Fedora和R ...
- Git&GitHub语法大全
目录 1. GitHub与Git万用语法 1)创建库 2)添加和提交到仓库 3)版本回退 4)缓存区和暂存区 5)撤销和删除文件 6)远程仓库 7)创建和合并分支 2. 更多Git语法 1. GitH ...
- ELK+Kafka 企业日志收集平台(一)
背景: 最近线上上了ELK,但是只用了一台Redis在中间作为消息队列,以减轻前端es集群的压力,Redis的集群解决方案暂时没有接触过,并且Redis作为消息队列并不是它的强项:所以最近将Redis ...
- (转)Spring Boot(二):Web 综合开发
http://www.ityouknow.com/springboot/2016/02/03/spring-boot-web.html 上篇文章介绍了 Spring Boot 初级教程:Spring ...
- C# - 汉字与unicode之间的转换
/// <summary> /// 字符串转Unicode码 /// </summary> /// <returns>The to unicode.</ret ...