Scala | 字符串揭祕

轉載自:http://songkun.me/2018/05/14/scala-string-ops/

String 源碼剖析

Scala 中的 String 其實僅僅是 java.lang.String 的別名,在 scala.Predef 中可以找到其定義:

1
type String = java.lang.String

明明 Scala 的字符串有很多非常有用的函數啊,難道都是錯覺?

實際上 scala.Predef 中還有一個與 String 關係重大的隱式函數:

1
@inline implicit def augmentString(x: String): StringOps = new StringOps(x)

該函數將 String 值隱式轉換爲 StringOps 值,StringOps 源碼如下:

1
2
3
4
final class StringOps(override val repr: String) extends AnyVal with StringLike[String] {
  ...
  override def apply(index: Int): Char = repr charAt index
  override def slice(from: Int, until: Int): String = ???

除了 slice 函數以外,其餘函數我們也沒怎麼用過,還得繼續深入一層,看下 StringLike 的源碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
trait StringLike[+Repr] extends Any with scala.collection.IndexedSeqOptimized[Char, Repr] with Ordered[String] {
self =>

  override def mkString = toString

  override def slice(from: Int, until: Int): e

  def * (n: Int): String = 

  def stripLineEnd: String

  def linesWithSeparators: Iterator[String]

  def capitalize: String

  def stripPrefix(prefix: String)

  def stripSuffix(suffix: String)

  def replaceAllLiterally(literal: String, replacement: String): String

  def stripMargin(marginChar: Char): String

  def stripMargin: String = stripMargin('|')
  
  ...
}

臥槽,我們平時常用的函數都在這裏啊,原來如此!

其實這是 Scala 的一種常見設計模式,即 Type Class 模式(源於 Haskell),通過 implicit 轉換函數,在不修改第三方庫源碼(java.lang.String)的條件下,實現對第三方庫功能增強,用在 String 這裏非常適合。

常用函數

下面是一些 Scala String 的常用函數,非常方便。

多行字符串

使用 """ 包裹即可實現多行字符串,比 Java 方便太多:

1
2
3
4
5
6
val s =
  """
    This is
    a scala
    line
  """

輸出如下:

1
2
3
4
s: String = 
    This is
    a scala
    line

若想要 去掉 每行開頭的空字符串,可以用 stripMargin

1
2
3
4
5
6
val a =
  """
    |This is  // 使用默認分割符 |
    |a scala
    |line
  """.stripMargin

輸出如下:

1
2
3
4
a: String = 
This is
a scala
line

stripMargin 函數實現如下:

1
2
3
4
5
6
/** For every line in this string:
  *
  *  Strip a leading prefix consisting of blanks or control characters
  *  followed by `|` from the line.
  */
def stripMargin: String = stripMargin('|')

因此,不必非要用 | 分割符,可以使用自定義的分隔符:

1
2
3
4
5
6
val b =
  """
    #This is
    #a scala
    #line
  """.stripMargin('#')

輸出與使用 | 完全相同:

1
2
3
4
b: String = 
This is
a scala
line

字符串插值

字符串插值是通過將 String 隱式轉換爲 StringContext 實現的,Scala 允許我們自定義字符串插值器。

1. s 插值

在任意字符串前面加 s 後,就可以在該字符串中使用 變量 和 表達式 了:

1
2
3
val a = 111
val b = 6
val c = s"a + b = ${a + b}"

2. f 插值

在字符串前面加 f,以簡單格式化字符串:

1
2
3
val a = 111.111111
val b = 6
val c = f"a * b = ${a * b}%2.2f"
  • %2.2f 指定格式

3. raw 插值

對字符串中的字符 不做編碼,其他與 s 插值相同。

若有 s 插值如下:

1
2
3
4
5
6
7
val a = 111
val b = s"b = \n$a"

// 輸出
a: Int = 111
b: String = b = 
111

若不想對 \n 轉義,則可用 raw 插值:

1
2
3
4
5
6
val a = 111
val b = raw"b = \n$a"

// 輸出
a: Int = 111
b: String = b = \n111

strip

去掉字符串前後綴:

1
". Hello .".stripPrefix(".").stripSuffix(".").trim
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章