转载自: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 |