文件和正则表达式

摘要:

在本篇中,你将学习如何执行常用的文件处理任务,比如从文件中读取所有行或单词,或者读取包含数字的文件等。本篇的要点包括:

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学习(九)---文件和正则表达式的更多相关文章

  1. Scala学习九——文件和正则表达式

    一.本章要点 Source.fromFile(...).getLines.toArray输出文件的所有行; Source.fromFile(...).mkString以字符串形式输出文件内容; 将字符 ...

  2. Scala学习(九)练习

    文件正则表达式&练习 1. 编写一小段Scala代码,将某个文件中的行倒转顺序,将最后一行作为第一行,依此类推 程序代码: import scala.io.Source import java ...

  3. Scala学习笔记--文件IO

    补充: http://blog.csdn.net/lyrebing/article/details/20369445 http://developer.51cto.com/art/200907/134 ...

  4. 初步学习pg_control文件之九

    接前文,初步学习pg_control文件之八 来看这个: pg_time_t time; /* time stamp of last pg_control update */ 当初初始化的时候,是这样 ...

  5. 机器学习(三)--- scala学习笔记

    Scala是一门多范式的编程语言,一种类似Java的编程语言,设计初衷是实现可伸缩的语言.并集成面向对象编程和函数式编程的各种特性. Spark是UC Berkeley AMP lab所开源的类Had ...

  6. 【大数据】Scala学习笔记

    第 1 章 scala的概述1 1.1 学习sdala的原因 1 1.2 Scala语言诞生小故事 1 1.3 Scala 和 Java  以及 jvm 的关系分析图 2 1.4 Scala语言的特点 ...

  7. 【Scala】Scala学习资料

    Scala学习资料 java 树形 分类器_百度搜索 决策树分类器-Java实现 - CSDN博客 KNN分类器-Java实现 - CSDN博客 学习:java设计模式—分类 - 飞翔荷兰人 - 博客 ...

  8. 初步学习pg_control文件之十

    接前文 初步学习pg_control文件之九 看下面这个 XLogRecPtr checkPoint; /* last check point record ptr */ 看看这个pointer究竟保 ...

  9. Scala学习随笔——Scala起步

    实验楼学习Scala语言的笔记,课程网址为https://www.shiyanlou.com/courses/490 一.Scala简介 Scala 是一门多范式的编程语言,类似于 Java .设计初 ...

随机推荐

  1. Android Studio多渠道打包(一)

    1. 多渠道的概念 APP发布到不同的应用平台,监测用户是从哪个平台安装的. 2. 为什么要多渠道打包 统计用户安装APP来源 批量修改生成的apk文件名 可更改包名 生成不同应用名称或图标 3.多渠 ...

  2. git 入门教程之撤销更改

    撤销更改 相信你已经了解了 git 的基本概念,也清楚了工作区,暂存区和版本库的关系,现在让我们用所学的知识继解决实际问题吧! 背景 正常看得见的目录是我们最为熟悉的工作区,在工作中不可能总是100% ...

  3. Android 7.0 fiddler代理抓不到https请求的解决办法

    解决方法:  1.在源码res目录下新建xml目录,增加network_security_config.xml文件 (工程名/app/src/main/res/xml/network_security ...

  4. Linux中对逻辑卷的移除

    移除前先df -mT 看一下:(在上一篇的基础上:Linux中对逻辑卷进行扩容) 1.取消挂载同时删除/etc/fstab下的记录 取消挂载 umount /dev/zhi/lv-zhi 删除记录 v ...

  5. [20190227]简单探究tab$的bojb#字段.txt

    [20190227]简单探究tab$的bojb#字段.txt --//上午做了删除tab$表,其对应索引i_tab1的恢复,我一直以为这个索引会很大,没有想到在我的测试环境仅仅139个键值.--//查 ...

  6. C#-封装(七)

    封装概念 C#是面向对象的一门语言,面向对象的语言有三大特性:封装.继承.多态.而封装可以实现一个自定义的类,从而定义新的对象 封装是将一个或多个项目集合在一个单元中,这个单元称之为类.这样可以防止对 ...

  7. Java的sql动态参数

    在C#的方法中可以使用params Parameter[] values来动态获取sql语句中的参数值数组.Java中可以自己封装出一个类似于C#的方法 1.获取结果集 /** * 获取结果集 * @ ...

  8. web前端(9)—— CSS属性

    属性 终于到css属性,前面就零零散散的用了什么color,font-size之类,本篇博文就专项的介绍它了 字体属性 font-family 此属性是设置字体样式的,比如微软雅黑,方正书体,华文宋体 ...

  9. [C#]List的Sort()、Find()、FindAll()、Exist()的使用方法举例

    [C#基础]List的Sort().Find().FindAll().Exist()的使用方法举例 List函数用得还是比较多的,正好用到其中的向个方法,做了一个例程,再总结一下: 先建一个学生类: ...

  10. SQL Server 锁实验(INSERT加锁探究)

    insert语句: 其上锁情况为: insert语句会对表上的所有索引作出更新,因此这里看到的索引列较多,我们先把所有的索引搞出来看看: 可以看到所有索引都涉及到了,然后我们来仔细分析下加锁情况: 1 ...