[转帖]学习Scala脚本:从文件里读取行记录_Android, Python及开发编程讨论区_Weblogic技术|Tuxedo技术|中间件技术|Oracle论坛|JAVA论坛|Linux/Unix技术|hadoop论坛_联动北方技术论坛  
网站首页 | 关于我们 | 服务中心 | 经验交流 | 公司荣誉 | 成功案例 | 合作伙伴 | 联系我们 |
联动北方-国内领先的云技术服务提供商
»  游客             当前位置:  论坛首页 »  自由讨论区 »  Android, Python及开发编程讨论区 »
总帖数
1
每页帖数
101/1页1
返回列表
0
发起投票  发起投票 发新帖子
查看: 3694 | 回复: 0   主题: [转帖]学习Scala脚本:从文件里读取行记录        下一篇 
刘伟
注册用户
等级:少校
经验:938
发帖:82
精华:0
注册:2013-6-24
状态:离线
发送短消息息给刘伟 加好友    发送短消息息给刘伟 发消息
发表于: IP:您无权察看 2013-7-2 9:26:18 | [全部帖] [楼主帖] 楼主

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

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

import scala.io.Source
if (args.length > 0) {
      for (line <- Source.fromFile(args(0)).getLines)
      print(line.length + " " + line)
}
else
Console.err.println("Please enter filename")


代码 3.10 从文件中读入行

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

$ scala countchars1.scala countchars1.scala


你会看到:

import scala.io.Source
if (args.length > 0) {
      for (line <- Source.fromFile(args(0)).getLines)
      print(line.length + " " + line)
}
else
Console.err.println("Please enter filename")


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

import scala.io.Source
if (args.length > 0) {
      for (line <- Source.fromFile(args(0)).getLines)
      print(line.length + " " + line)
}
else
Console.err.println("Please enter filename")


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

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


最后的toList是必须加的,因为getLines方法返回的是枚举器。一旦你使用它完成遍历,枚举器就失效了。而通过调用toList把它转换为List,你就可以枚举任意次数,代价就是把文件中的所有行一次性贮存在内存里。lines变量因此就指向着包含了命令行指定的文件文本字串的数组。下一步,因为要对每行字符数计算两次,每个枚举计算一次,你或许会考虑把表达式拉出来变成一个小函数,专门用来计算传入字串的字符长度:

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


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

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


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

val longestLine = lines.reduceLeft(
(a, b) => if (a.length > b.length) a else b
)
val widths = lines.map(widthOfLength)


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

val maxWidth = widthOfLength(longestLine)


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

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


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

import scala.io.Source
def widthOfLength(s: String) = s.length.toString.length
if (args.length > 0) {
      val lines = Source.fromFile(args(0)).getLines.toList
      val longestLine = lines.reduceLeft(
      (a, b) => if (a.length > b.length) a else b
      )
      val maxWidth = widthOfLength(longestLine)
      for (line <- lines) {
            val numSpaces = maxWidth widthOfLength(line)
            val padding = " " * numSpaces
            print(padding + line.length +" | "+ line)
      }
}
else
Console.err.println("Please enter filename")




赞(0)    操作        顶端 
总帖数
1
每页帖数
101/1页1
返回列表
发新帖子
请输入验证码: 点击刷新验证码
您需要登录后才可以回帖 登录 | 注册
技术讨论