Chap 9 文件/正则表达式
focus on
Source.fromFile(..).getLines.toArray 输出文件的所有行
Source.fromFile(..).mkString 以字符串形式输出文件内容
将字符串转换为数字, 可以用 toInt 或 toDouble 方法
使用 Java 的 PrintWriter 来写入文本文件
"正则".r 是一个 Regex 对象
正则表达式 包含 或者 “ , 使用 """..."""
如果正则模式包含分组, 你可以用如下语法来提取它们的内容 for (regex(变量1, ...) <- 字符串)
9.1 Read Line、Character
import java.io.PrintWriter
import java.io.File
import scala.io.Source
object HelloFile extends App {
val writer = new PrintWriter(new File("test.txt")) // base dir will generate test.txt
writer.write("Hello Scala In test.txt")
writer.close()
// 9.0 console read
print("Please enter your input : ")
val line = Console.readLine
println("Thanks, you just typed: " + line)
// 9.1 Read File content
println("Following is the content read:")
Source.fromFile("test.txt").foreach {
print
}
// mkString / Array
val source = Source.fromFile("test.txt")
val contents = source.mkString
//
// val arr = source.getLines().toArray
//
println("\n\ncontents : " + contents + "\n")
//
// println("\n\narr : " + arr)
source.close()
// 9.2 读取字符
val source2 = Source.fromFile("test.txt")
val iterator = source2.buffered
while (iterator.hasNext) {
if (iterator.head != 'e') {
print(iterator.next())
} else {
iterator.next()
}
}
source2.close()
}
9.2 Read Word/Number/URL
object ReadLine extends App {
val source = Source.fromFile("num.txt", "UTF-8")
// 9.3 读取词法单元 和 数字
val tokens = source.mkString.split("\\s+")
val numbers = tokens.map(_.toDouble)
for (w <- numbers) {
println(w)
}
/* Output
12.0
34.0
56.0
78.0
89.0
*/
source.close()
println("haow old are you ?")
val age = readInt()
println("age : " + age)
// 9.4 从 URL 或 其他源 读取
val source1 = Source.fromURL("http://52binge.com", "UTF-8") // URL 需要事先 知道字符集
val source2 = Source.fromString("Hello,World")
val source3 = Source.stdin
}
9.3 BinaryFile/PrintWrite
object BinaryFileAndPrintWriteFile extends App {
// 9.5 读取二进制文件 (使用Java类库)
val file = new File("num.txt")
val in = new FileInputStream(file)
val bytes = new Array[Byte](file.length().toInt) // File readTo 字符数组
in.read(bytes)
for (c <- bytes) {
println(c.toChar)
}
in.close()
// 9.6 写入文本文件
val out = new PrintWriter("numbers.txt")
for (i <- 1 to 10) {
out.println(i)
}
out.close()
}
9.4 Serialize Object
object DirSerialize {
// 9.7 访问目录, (Scala不是非常方便)
// 9.8 序列化
// 1. Java 序列化, public class Person implements java.io.Serializable
// 2. Scala 序列化, @SerialVersionUID(42L) class Person extends Serializable
/*
val fred = new Person(...)
import java.io._
val out = new ObjectOutputStream(new FileOutputStream("/tmp/test.obj"))
out.writeObject(fred)
out.close()
val in = new ObjectInputStream(new FileOInputStream("/tmp/test.obj"))
val savedFred = in.readObject().asInstanceOf[person]
*/
// Scala 集合类都是可序列化的, 因此 你可以把它们用做你的可序列化类的成员
/*
class Person extends Serializable {
private val friends = new ArrayBuffer[Person] // ok--ArrayBuffer 是可序列化的
*/
}
9.5 Match 正则
object Match extends App {
val numPattern = "[0-9]".r
val wsnumwsPattern = """\s+[0-9]+\s""".r
for (matchString <- numPattern.findAllIn("99 bottles, 98 bottles")) {
println(matchString)
}
for (matchString <- wsnumwsPattern.findAllIn("99 bottles, 998 9bottles")) {
println("B" + matchString + "B")
}
// 9.10 正则 表达式 组
val numitemPattern = "([0-9]+) ([a-z]+)".r
val numitemPattern(num, item) = "99 bottles"
for (numitemPattern(num, item) <- numitemPattern.findAllIn("99 bottles, 998 9bottles")) {
println(num + " " + item)
}
}
Chap 10 Trait
focus on
Class 可以实现任意数量的 Trait
Trait 可以要求实现它们的 Class 具备特定的 字段、方法、超类
Scala Trait 可提供 方法 和 字段 的实现
多个 Trait 叠加, 顺序很重要。 方法 先被执行的 Trait 排在更后面
Scala 提供 “Trait” 而非 Interface。Trait 可同时提供 抽象方法 和 具体方法, 而 Class 可以实现多个 Trait
10.1 当做接口使用的 Trait
trait Logger {
def log(msg: String) // 这是个抽象方法, 你不需要声明为 abstract, 特质中未被实现的方法就是 抽象方法
}
/**
* Date : 2016-04-07
* 所有的 Java 接口 都可以 作为 Scala trait 使用
*/
class ConsoleLogger extends Logger with Cloneable with Serializable {
// use extends
def log(msg: String): Unit = { // 重写 Trait 的 抽象方法 不需要给出 override 关键字
println(msg)
}
// 和 Java 一样, Scala 类 只能有一个 超类, 但 可以有 任意数量的 Trait
}
10.2 带有具体实现的 Trait
trait Logged {
def log(msg: String) {}
}
trait ConsolePrintLogger extends Logged {
override def log(msg: String): Unit = {
println(msg)
}
}
class SavingsAccount extends Account with ConsolePrintLogger {
def withdraw(amount: Double): Unit = {
if (amount > balance) {
log("Insufficient funds")
} else {
balance -= amount
}
}
// 注 : 让 Trait 存在具体行为存在一个 弊端. 当 Trait 改变时, 所有混入了该 Trait 的 Class 都必须 re compile
}
10.3 带有 Trait 的对象
class SavingsAcc extends Account with Logged {
def withdraw(amount: Double): Unit = {
if (amount > balance) {
log("Insufficient funds")
} else {
balance -= amount
}
}
}
// 看上去,毫无意义, 其实不然,你可以在 构造对象 的时候 "混入" 一个更好的 日志 记录器
object SavingsAcc extends App {
val acct1 = new SavingsAcc with ConsolePrintLogger
// val acct2 = new SavingsAcc with FileLogger
acct1.withdraw(100)
}
10.4 叠加在一起的 Trait
你可以为 Class 或 Object 添加多个互相调用的特质。
trait TimestampLogger extends Logged {
override def log(msg: String) {
super.log(new java.util.Date() + " " + msg)
}
}
trait ShortLogger extends Logged {
val maxLength = 15 // 关于 Trait 的 Field
override def log(msg: String) {
super.log(if (msg.length <= maxLength) msg else msg.substring(0, maxLength - 3) + "...")
}
}
上述 log 方法 每一个都将修改过的消息传递给 super.log
对 Trait 而言, super.log 并不像 Class 那样拥有相同的含义。
实际上, super.log 调用的是 Trait层级 中的下一个 Trait,具体是哪一个具体 Trait 的顺序决定
val acct1 = new SavingsAccount with ConsolePrintLogger with TimestampLogger with ShortLogger
output :
Sun Feb 06 17:45:45 ICT 2011 Insufficient...
10.5 在特质中重写抽象方法
trait Logger {
def log(msg: String) // 这是个抽象方法
}
trait TimestampLogger extends Logged {
override def log(msg: String) {
super.log(new java.util.Date() + " " + msg)
}
}
编译器将 super.log 调用标记为 错误。Logger.log 方法没有实现。我们没法知道哪个 log 方法会被调用
Scala 认为 TimestampLogger 依然是 抽象 的, 因为你必须给 方法 打上 abstract override
trait TimestampLogger extends Logged {
abstract override def log(msg: String) {
super.log(new java.util.Date() + " " + msg)
}
}
10.6 当做富接口使用的Trait
trait Logger {
def log(msg: String)
def info(msg: String) { log("INFO: " + msg) }
def warn(msg: String) { log("WARN: " + msg) }
def severe(msg: String) { log("SEVERE: " + msg) }
使用 Logger 的 Class 就可以任意 调用这些 log 方法了。
class SavingsAccount extends Account with Logged {
def withdraw(amount: Double): Unit = {
if (amount > balance) {
severe("Insufficient funds")
} else {
balance -= amount
}
}
...
override def log(msg: String) { println(msg); }
}
Scala 这种 Trait 使用 具体 和 抽象 方法随处可见
10.7 Trait 中的具体 Field
Trait 中的 Field 可以是具体的,也可以是抽象的。
trait ShortLogger extends Logged {
val maxLength = 15 // 具体的字段
...
}
class SavingsAccount extends Account with ConsolePrintLogger with ShortLogger {
var interest = 0.0
def withdraw(amount: Double): Unit = {
if (amount > balance) {
log("Insufficient funds")
} else {
balance -= amount
}
}
}
你可以把具体的特质字段当做是针对使用该特质的类的 “装配指令”。任何混入的字段都自动成为该类 自己 的 字段
10.8 Trait 中的抽象字段
Trait 中未被初始化的字段,在具体的子类中 必须被 override (不需要写该关键字)
trait ShortLogger extends Logged {
val maxLength: Int // 抽象字段
override def log(msg: String) {
super.log(
if (msg.length <= maxLength) msg
else msg.substring(0, maxLength - 3 ) + "..."
) // 实现中使用了 maxLength
}
...
}
在 一个 具体的 Class 使用该 Trait 中,你必须提供 maxLength 字段
class SavingsAccount extends Account with ConsoleLogger with ShortLogger {
val maxLength = 20 // 不需要写 override
...
}
10.9 Trait 构造顺序
trait FileLogger extends Logger {
val out = new PrintWrite("app.log") // 这是 特质构造器的一部分
out.println("# " + new Date().toString) // 同样是特质构造器的一部分
def log(msg: String) {
out.println(msg)
out.flush()
}
}
构造顺序
class SavingsAccount extends Account with FileLogger with ShortLogger
Account (超类)
Logger (第一个 Trait 的 父 Trait)
FileLogger (第一个 Trait)
ShortLogger (第二个 Trait)。 注意 它的 父trait Logger 已被构造
SavingsAccount (Class)
说明 : 构造器的顺序是 类 的 线性化 的反向
10.10 初始化 Trait 中的字段
Trait 不能有构造器参数. 每个 Trait 都有一个无参数的构造器
说明 : 缺少构造器参数是 Trait 与 Class 的唯一技术差别
这个局限对于那些需要某种定制才有用的 Trait 来说会是一个问题
10.11 扩展 Class 的 Trait
trait 可以扩展另一个 trait, 而由 trait 组成的继承层级也很常见。而由 trait 扩展 class, 这个 class 会自动成为所有混入该 trait 的 超类
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。