学习Scala脚本:从文件里读取行记录

开发 后端
本文节选自Martin Odersky,Lex Spoon和Bill Venners所著,Regular翻译的《Programming in Scala》的第二章。Scala是一种针对 JVM 将函数和面向对象技术组合在一起的编程语言。

处理琐碎的,每日工作的脚本经常需要处理文件。本节中,你将建立一个从文件中读入行记录,并把行中字符个数前置到每一行,打印输出的脚本。***版展示在代码3.10中:

51CTO编辑推荐:Scala编程语言专题

  1. import scala.io.Source  
  2.  
  3. if (args.length > 0) {  
  4.  
  5.  for (line <- Source.fromFile(args(0)).getLines)  
  6.   print(line.length + " " + line)  
  7. }  
  8. else  
  9.  Console.err.println("Please enter filename")  

代码 3.10 从文件中读入行

此脚本开始于从包scala.io引用名为Source的类。然后检查是否命令行里定义了至少一个参数。若是,则***个参数被解释为要打开和处理的文件名。表达式Source.fromFile(args(0)),尝试打开指定的文件并返回一个Source对象,你在其上调用getLines。函数返回Iterator[String],在每个枚举里提供一行包括行结束符的信息。for表达式枚举这些行并打印每行的长度,空格和这行记录。如果命令行里没有提供参数,***的else子句将在标准错误流中打印一条信息。如果你把这些代码放在文件contchars1.scala,并运行它调用自己:

  1. $ scala countchars1.scala countchars1.scala 

你会看到:

  1. 23 import scala.io.Source  
  2. 1  
  3. 23 if (args.length > 0) {  
  4. 1  
  5. 50   for (line <- Source.fromFile(args(0)).getLines)  
  6. 36     print(line.length + " " + line)  
  7. 2 }  
  8. 5 else  
  9. 47 Console.err.println("Please enter filename")  

尽管当前形式的脚本打印出了所需的信息,你或许希望能让数字右序排列,并加上管道符号,这样输出看上去就替换成:

  1. 23 | import scala.io.Source  
  2.  1 |  
  3. 23 | if (args.length > 0) {  
  4.  1 |  
  5. 50 |   for (line <- Source.fromFile(args(0)).getLines)  
  6. 36 |     print(line.length + " " + line)  
  7.  2 | }  
  8.  5 | else  
  9. 47 |   Console.err.println("Please enter filename")  

想要达到这一点,你可以对所有行枚举两次。***次决定每行字符计数的***宽度。第二次打印输出之前计算的***宽度。因为要枚举两次,你***把它们赋给变量:

  1. val lines = Source.fromFile(args(0)).getLines.toList 

***的toList是必须加的,因为getLines方法返回的是枚举器。一旦你使用它完成遍历,枚举器就失效了。而通过调用toList把它转换为List,你就可以枚举任意次数,代价就是把文件中的所有行一次性贮存在内存里。lines变量因此就指向着包含了命令行指定的文件文本字串的数组。

下一步,因为要对每行字符数计算两次,每个枚举计算一次,你或许会考虑把表达式拉出来变成一个小函数,专门用来计算传入字串的字符长度:

  1. def widthOfLength(s: String) = s.length.toString.length 

有了这个函数,你就可以计算***长度了:

  1. var maxWidth = 0 
  2. for (line <- lines)  
  3.  maxWidthmaxWidth = maxWidth.max(widthOfLength(line)) 

这里你用一个for表达式枚举了每一行,计算这些行的宽度,并且,如果比当前***宽度还大,就把它赋值给maxWidth,一个初始化为0的var。(max方法是你可以在任何Int上调用的,可以返回被调用者和被传入者中的较大的值。)如果你希望不用var发现***值,替代的方法是可以首先找到最长的一行,如:

  1. val longestLine = lines.reduceLeft(  
  2.  (a, b) => if (a.length > b.length) a else b  
  3. )  
  4.  
  5. val widths = lines.map(widthOfLength)  

reduceLeft方法把传入的方法应用于lines的前两个元素,然后再应用于***次应用的结果和lines接下去的一个元素,等等,直至整个列表。每次这样的应用,结果将是碰到的最长一行,因为传入的函数,(a, b) => if (a.length > b.length) a else b,返回两个传入字串的最长那个。reduceLeft将传回***一次应用的结果,也就是本例lines中包含的最长字串。

得到这个结果之后,你可以通过把最长一行传给widthOfLength计算***的宽度:

  1. val maxWidth = widthOfLength(longestLine) 

***剩下的就是用一个合适的格式把这些行打印出来。你可以这么做:

  1. for (line <- lines) {  
  2.  val numSpaces = maxWidth - widthOfLength(line)  
  3.  val padding = " " * numSpaces  
  4.  print(padding + line.length + " | " + line)  

在这个for表达式里,你再一次枚举了全部行记录。对于每一行,首先计算行长度前所需的空格并把它赋给numSpaces。然后用表达式:" " * numSpaces创建包含numSpaces个空格的字串。最终,你打印出你想要格式的信息。全部的脚本展示在代码3.11中:

  1. import scala.io.Source  
  2. def widthOfLength(s: String) = s.length.toString.length  
  3. if (args.length > 0) {  
  4.  val lines = Source.fromFile(args(0)).getLines.toList  
  5.  val longestLine = lines.reduceLeft(  
  6.   (a, b) => if (a.length > b.length) a else b  
  7.  )  
  8.  val maxWidth = widthOfLength(longestLine)  
  9.  for (line <- lines) {  
  10.   val numSpaces = maxWidth widthOfLength(line)  
  11.   val padding = " " * numSpaces  
  12.   print(padding + line.length +" | "+ line)  
  13.  }  
  14. }  
  15. else  
  16.  Console.err.println("Please enter filename")  

代码 3.11 对文件的每行记录打印格式化的字符数量。

本文节选自《Programming in Scala》

【相关阅读】

  1. 学习识别Scala的函数式风格
  2. Scala编程实例:使用Set和Map
  3. Scala编程实例:使用List和Tuple
  4. Scala编程实例:带类型的参数化数组
  5. 初探Scala编程:编写脚本,循环与枚举
责任编辑:yangsai 来源: Artima
相关推荐

2009-08-21 16:17:25

ScalaTwitter API

2009-09-28 11:01:39

从Java走进Scal

2020-01-10 22:56:56

Python图像处理Linux

2009-06-17 11:44:22

Scala控制结构

2021-04-21 08:03:34

脚本Shell读取

2021-08-20 10:46:25

Shell脚本文件Linux

2009-02-04 17:32:03

ibmdwJavaScala

2009-07-15 10:14:25

Scala并发性

2009-12-09 09:15:47

从Java走进ScalTwitter API

2009-06-16 17:54:38

Scala类语法语义

2009-11-16 17:04:46

Inside Scal

2009-06-11 17:39:55

xmljava

2009-07-21 16:58:31

Scala变量范围

2009-07-22 07:47:00

Scala客户代码

2009-07-22 07:43:00

Scala闭包

2009-07-08 17:40:28

ScalaScala脚本

2009-07-22 09:02:45

Scala组合继承

2011-07-07 17:30:25

iPhone Xcode

2009-07-22 07:57:00

ScalaCurry化函数

2009-07-08 15:35:18

Case类Scala
点赞
收藏

51CTO技术栈公众号