ZetCode

Scala 字符串

最后修改于 2023 年 1 月 10 日

在本文中,我们将介绍如何在 Scala 中处理字符串。

Scala 字符串定义

在 Scala 中,String 是 Unicode 字符序列。字符串是对象。有两种基本的字符串处理类型:

String 是一个不可变的 Unicode 字符序列。StringBuilder 是一个可变的 Unicode 字符序列。

字符串字面量是源代码中用双引号括起来的一系列字符。例如,“falcon”就是一个字符串字面量。

Scala 字符串简单示例

第一个示例是一个简单的 Scala 字符串示例。

main.scala
@main def main() =

    val w = "an old falcon"
    println(w)

    val msg = "an " + "old " + "falcon"
    println(msg)

我们定义了两个字符串。

val w = "an old falcon"
println(w)

字符串字面量绑定到 w 标识符。字符串字面量用双引号括起来。它使用 println 打印到控制台。

val msg = "an " + "old " + "falcon"

我们使用 + 运算符将三个字符串连接成一条消息。

$ scala main.scala
an old falcon
an old falcon

Scala 可变和不可变字符串

String 是一个不可变的 Unicode 字符序列,而 StringBuilder 是一个可变的 Unicode 字符序列。下一个示例将展示区别。

main.scala
import scala.collection.mutable.StringBuilder

@main def main() =

    val name = "Jane"
    val name2 = name.replace('J', 'K')
    val name3 = name2.replace('n', 't')

    println(name)
    println(name3)

    val sb = StringBuilder("Jane")
    println(sb)

    sb.setCharAt(0, 'K')
    sb.setCharAt(2, 't')

    println(sb)

这两个对象都有替换字符串中字符的方法。

val name = "Jane"
val name2 = name.replace('J', 'K')
val name3 = name2.replace('n', 't')

String 上调用 replace 方法会返回一个新的修改后的字符串。原始字符串不会被更改。

sb.setCharAt(0, 'K')
sb.setCharAt(2, 't')

StringBuildersetCharAt 方法将用新字符替换给定索引处的字符。原始字符串会被修改。

$ scala main.scala
Jane
Kate
Jane
Kate

Scala 多行字符串

可以使用三引号创建多行字符串。为了删除前导空格,我们使用 | 字符和 stripMargin 方法。

main.scala
@main def main() =

    val sonnet55 =
        """Not marble nor the gilded monuments
        |Of princes shall outlive this powerful rhyme,
        |But you shall shine more bright in these contents
        |Than unswept stone besmeared with sluttish time.
        |When wasteful war shall statues overturn,
        |And broils root out the work of masonry,
        |Nor Mars his sword nor war's quick fire shall burn
        |The living record of your memory.
        |'Gainst death and all-oblivious enmity
        |Shall you pace forth; your praise shall still find room
        |Even in the eyes of all posterity
        |That wear this world out to the ending doom.
        |So, till the Judgement that yourself arise,
        |You live in this, and dwell in lovers' eyes.""".stripMargin

    println(sonnet55)

我们在多行字符串中有一个诗句。

$ scala main.scala
Not marble nor the gilded monuments
Of princes shall outlive this powerful rhyme,
But you shall shine more bright in these contents
...

Scala 字符串转义字符

转义字符是执行特定操作的特殊字符。例如,\n 字符会开始新的一行。

main.scala
@main def main() =

    println("Three\t bottles of wine")
    println("He said: \"I love ice skating\"")
    println("Line 1:\nLine 2:\nLine 3:")

我们有一个包含转义字符的示例。

println("Three\t bottles of wine")

\t 转义字符插入一个制表符。

println("He said: \"I love ice skating\"")

我们通过转义它们来在字符串字面量中插入双引号,使用 \

println("Line 1:\nLine 2:\nLine 3:")

使用 \n,我们创建了三行。

$ scala main.scala 
Three    bottles of wine
He said: "I love ice skating"
Line 1:
Line 2:
Line 3:

Scala 原生字符串

在原生字符串中,转义字符不会被解释。

main.scala
@main def main() = 

    println(raw"snow\tshow\tsnow")
    println("becomes") 
    println("snow\tshow\tsnow")

示例使用了一个原生字符串。

$ scala main.scala 
snow\tshow\tsnow
becomes
snow    show    snow

Scala 比较字符串

字符串自然地使用 ==!= 运算符进行比较。

main.scala
@main def main() = 

    val s1 = "Eagle"
    val s2 = "eagle"

    if s1 != s2 then 
        println("1) strings are not equal")

    if s1 == s2.capitalize then 
        println("2) strings are equal")

在示例中,我们比较了两个字符串。

$ scala main.scala 
1) strings are not equal
2) strings are equal

Scala 索引字符串字符

我们可以通过索引检索字符串字符。索引从零开始。

main.scala
@main def main() = 

    val w = "and old falcon"

    println(w(0))
    println(w(5))

在示例中,我们打印了第一个和第六个字符。

Scala 分割字符串

split 函数根据指定的分隔符将字符串分割成字符串列表。

main.scala
@main def main() =

    val word = "eagle,falcon,hawk,owl"
    val birds = word.split(",")

    birds.foreach(println)

我们有一个由逗号分隔的鸟类组成的字符串。我们将字符串分割以单独获取所有鸟类。

$ scala main.scala
eagle
falcon
hawk
owl

Scala 字符串 startsWith/endsWith

startsWith 方法如果字符串以指定的前缀开头,则返回 true,endsWith 方法如果字符串以指定的字符结尾,则返回 true。

main.scala
@main def main() =

    val words = List("tank", "boy", "tourist", "ten",
            "pen", "car", "marble", "sonnet", "pleasant",
            "ink", "atom")

    val res = words.filter(e => startWithT(e))
    println(res)

    val res2 = words.filter(e => endWithK(e))
    println(res2)


val startWithT = (word: String) => word.startsWith("t")
val endWithK = (word: String) => word.endsWith("k")

我们有一系列单词。我们找出哪些单词以“t”开头,以“k”结尾。

val words = List("tank", "boy", "tourist", "ten",
        "pen", "car", "marble", "sonnet", "pleasant",
        "ink", "atom")

我们有一系列单词。

val res = words.filter(e => startWithT(e))
println(res)

val res2 = words.filter(e => endWithK(e))
println(res2)

filter 函数会对它们的元素调用一个谓词。

val startWithT = (word: String) => word.startsWith("t")

startWithT 是一个自定义谓词函数,如果字符串以“t”开头,则返回 true。

$ scala main.scala
List(tank, tourist, ten)
List(tank, ink)

Scala 字符串插值

字符串插值是在字符串内用其值进行变量替换。插值字符串以 sf 前缀开头。

main.scala
@main def main() = 

    val name = "Peter"
    val age = 34

    println(s"$name is $age years old")

    val word = "falcon"

    println(s"The string has ${word.length} characters")

    val item = "beer"
    val price = 4.5

    println(s"The price of $item is $$$price")

在示例中,我们使用 s 前缀对字符串进行插值。

val name = "Peter"
val age = 34

println(s"$name is $age years old")

$name$age 变量被替换为其值。

println(s"The string has ${word.length} characters")

使用 {} 大括号,我们可以在字符串内使用表达式。

println(s"The price of $item is $$$price")

为了使用美元符号,我们使用 $$

$ scala main.scala 
Peter is 34 years old
The string has 6 characters
The price of beer is $4.5

使用 f 前缀,我们还在变量名后提供格式化字符。

main.scala
@main def main() = 

    val name = "Peter"
    val age = 34

    println(f"$name%s is $age%d years old")

我们使用 f 插值字符串构建一个字符串。

Scala 字符串转整数

我们使用 toInt 将字符串转换为整数。

main.scala
@main def main() =

    val vals = List[String | Int]("3", 12, "11", 5, 6, "8")

    val vals2 = vals.map(_.toString.toInt)
    println(vals2.sum)

    var msum = 0

    for e <- vals do
        msum = msum + e.toString.toInt

    println(msum)

我们有一个整数和字符串的列表。我们计算所有值的总和。

val vals2 = vals.map(_.toString.toInt)
println(vals2.sum)

在求和之前,我们需要使用 maptoInt 将元素转换为整数。我们使用内置的 sum 函数来计算总和。

var msum = 0

for e <- vals do
    msum = msum + e.toString.toInt

println(msum)

或者,我们使用 for 循环。

$ scala main.scala
45
45

Scala 字符串长度

对于许多字母,length 函数会给出正确的字符数。

main.scala
@main def main() =

    val word = "falcon"
    println(word.length)

    val word2 = "čerešňa"
    println(word2.length)

    val word3 = "ведомство"
    println(word3.length)

    val word4 = "合気道"
    println(word4.length)

在示例中,我们打印了英语、斯洛伐克语、俄语和日语单词的字符数。

$ scala main.scala
6
7
9
3

结果是正确的。


有些字母的 length 会给出错误的结果。

main.scala
import java.text.BreakIterator

@main def main() =

    val emojis = "🐜🐬🐄🐘🦂🐫🐑🦍🐯🐞"
    println(emojis.length)

    val it = BreakIterator.getCharacterInstance
    it.setText(emojis)
    var count = 0

    while it.next != BreakIterator.DONE do
        count += 1

    println(count)

    println("--------------------------")

    val word = "नमस्ते"
    println(word.length)

    val it2 = BreakIterator.getCharacterInstance
    it2.setText(word)
    var count2 = 0

    while it2.next != BreakIterator.DONE do
        count2 += 1

    println(count2)

例如,对于表情符号和梵语单词,length 函数会给出错误的数字。我们可以使用 BreakIterator 获取表情符号的正确答案。然而,对于梵语单词,BreakIterator 仍然不正确。

$ scala main.scala
20
10
--------------------------
6
3

正确答案是 10 个表情符号字符和 4 个梵语字符。

Scala 字符串行

lines 方法返回一个流,其中包含从字符串中提取的行,由行终止符分隔。

main.scala
@main def main() =

    val words = """
        |club
        |sky
        |blue
        |cup
        |coin
        |new
        |cent
        |owl
        |falcon
        |brave
        |war
        |ice
        |paint
        |water""".stripMargin

    val wstream = words.lines()

    wstream.forEach(word =>

        if word.length() == 3 then
            println(word)
    )

我们在这段文本中有十四个单词。

var wstream = words.lines()

使用 lines 方法,我们创建了这个单词的流。

wstream.forEach(word =>

    if word.length() == 3 then
        println(word)
)

我们使用 forEach 遍历流,并打印所有长度为三个字母的单词。

$ scala main.scala
sky
cup
new
owl
war
ice

Scala 添加字符串

有几种方法可以添加或构建字符串。

main.scala
@main def main() = 

    val w1 = "an"
    val w2 = "old"
    val w3 = "falcon"

    val msg = w1 + " " + w2 + " " + w3
    println(msg)

    println(w1.concat(" ").concat(w2).concat(" ").concat(w3))

    println(s"$w1 $w2 $w3")
    println(f"$w1%s $w2%s $w3%s")
    printf("%s %s %s%n", w1, w2, w3)

在示例中,我们使用 + 运算符、concat 函数、字符串插值和 printf 函数构建字符串。

Scala 字符串匹配

matches 函数用于判断字符串是否与给定的正则表达式匹配。

main.scala
@main def main() =

    var words = """
            |book
            |bookshelf
            |bookworm
            |bookcase
            |bookish
            |bookkeeper
            |booklet
            |bookmark
            """.stripMargin

    var wstream = words.lines

    wstream.forEach(word =>

        if word.matches("book(worm|mark|keeper)?") then
            println(word)
    )

在示例中,我们打印了所有满足指定子模式的单词。

$ scala main.scala
book
bookworm
bookkeeper
bookmark

Scala 字符串字节

我们可以使用 getBytes 获取底层字节。

main.scala
@main def main() = 

    val w = "and old falcon"

    println(w.getBytes.mkString(" "))
    println(w.getBytes.map("%02x ".format(_)).mkString)

该示例以十进制和十六进制格式打印字符串的字节值。

println(w.getBytes.mkString(" "))

getBytes 返回一个字节数组。我们使用 mkString 将它们连接成一个字符串;值之间用空格分隔。

$ scala main.scala 
97 110 100 32 111 108 100 32 102 97 108 99 111 110
61 6e 64 20 6f 6c 64 20 66 61 6c 63 6f 6e 

Scala 连接字符串

我们可以使用 mkString 连接字符串列表。

main.scala
@main def main() =

    val words = List("work", "sky", "place", "cup")

    val joined = words.mkString(" ")
    println(joined)

    val joined2 = words.mkString(", ")
    println(joined2)

在示例中,我们将一个字符串列表连接成一个字符串。

val joined = words.mkString(" ")

mkString 方法的参数是一个分隔符,它将分隔最终字符串中的每个字符串。

$ scala main.scala
work sky place cup
work, sky, place, cup

在本文中,我们介绍了如何在 Scala 中处理字符串。