Chap 9 文件/正则表达式

focus on

  1. Source.fromFile(..).getLines.toArray 输出文件的所有行

  2. Source.fromFile(..).mkString 以字符串形式输出文件内容

  3. 将字符串转换为数字, 可以用 toInt 或 toDouble 方法

  4. 使用 Java 的 PrintWriter 来写入文本文件

  5. "正则".r 是一个 Regex 对象

  6. 正则表达式 包含 或者 “ , 使用 """..."""

  7. 如果正则模式包含分组, 你可以用如下语法来提取它们的内容 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)
  }

}

MyGithub

Chap 10 Trait

focus on

  1. Class 可以实现任意数量的 Trait

  2. Trait 可以要求实现它们的 Class 具备特定的 字段、方法、超类

  3. Scala Trait 可提供 方法字段 的实现

  4. 多个 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

  1. Account (超类)

  2. Logger (第一个 Trait 的 父 Trait)

  3. FileLogger (第一个 Trait)

  4. ShortLogger (第二个 Trait)。 注意 它的 父trait Logger 已被构造

  5. SavingsAccount (Class)

说明 : 构造器的顺序是 类 的 线性化 的反向

10.10 初始化 Trait 中的字段

Trait 不能有构造器参数. 每个 Trait 都有一个无参数的构造器

说明 : 缺少构造器参数是 Trait 与 Class 的唯一技术差别

这个局限对于那些需要某种定制才有用的 Trait 来说会是一个问题

10.11 扩展 Class 的 Trait

trait 可以扩展另一个 trait, 而由 trait 组成的继承层级也很常见。而由 trait 扩展 class, 这个 class 会自动成为所有混入该 trait 的 超类


blair
209 声望31 粉丝

我是 Blair


引用和评论

0 条评论