Scala之String

1、前言正则表达式

  大数据领域的Spark、Kafka、Summingbird等都是由Scala语言编写而成,相比Java而言,Scala更精炼。因为笔者从事大数据相关的工做,因此有必要好好学习Scala语言,以前也学习过,可是没有记录,因此就会忘记,感受Scala确实比Java方便精炼不少,下面以Scala Cookbook英文版做为参考资料,从头至尾梳理Scala相关知识点,也加深印象。PS:这是在研究Zookeeper源码的间隙中交叉学习,不至于老是看源码太枯燥。编程

2、Stringapi

  在Scala的REPL环境中测试String的类型,能够发现其就是Java中的String。数组

      

  因此可使用Java中String的全部方法,如获取字符串的长度、链接多个字符串。在Scala中,因为String能够被隐式转化成StringOps类型,可将字符串当作一个字符序列,而且可使用foreach方法遍历字符串的每一个字符。app

      

  也可将字符串当成字符序列,使用for循环遍历每一个字符编程语言

      

  同理,也可将字符串当前字节序列,使用for循环遍历函数

      

  因为字符串能够当成字符顺序集合(字符序列),而在集合上可进行多种操做,如filter工具

  

  能够看到对字符串进行了过滤,去掉了l字符。filter方法是StringOps的方法,因为String会被隐式的转化为StringOps,所以能够调用此方法。学习

  对于该隐式转化,是在Perdef对象中定义。 测试

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

  向封闭类中添加方法

  在Java中,String被定义成final的,即没法继承String类或者添加任何方法,但在Scala中,咱们能够经过隐式转化添加方法,下面例子展现了Scala的String拥有String的特性和集合的特性。

      

  其中,drop方法和take方法都是Scala的序列(集合)的方法,而capitalize方法则是StringOps的方法,这种调用都是经过隐式转化完成的。

  2.1 测试String的相等性

  1. 问题描述

  你须要比较两个字符串以判断它们是否相等,或者它们包含相同的字符序列。

  2. 解决方案

  定义了以下字符串s一、s二、s3

  

  使用"=="进行相等性判断

  

  能够看到s一、s二、s3均相等,而对于一个好的"=="方法而言,即使是有参数为空,也不会抛出异常。

      

  若在比较过程当中不区分大小写,则可将字符串转化成大写或小写进行比较。

  

  然而,在对空字符串调用toUpperCase方法会抛出异常。

  

  在Java中,想要比较不区分大小写比较两个字符串是否相等,则使用equalsIgnoreCase方法

  

  3. 讨论

  在Scala中,咱们使用==方法来断定对象的相等性,这与Java不一样,Java使用equals方法断定两个对象的相等性,== 是比较两个对象是不是同一个对象(内存地址相同)。在Scala中,== 方法在AnyRef(全部引用类型的父类)中定义,其首先会检查空值,而后再调用第一个对象的equals方法进行比较,所以,当比较两个字符串是否相等时,咱们不须要检查空值。

  2.2 建立多行字符串

  1. 问题描述

  在Scala源码中想要建立多行字符串。

  2. 解决方案

  在Scala中,你可使用三个双引号来建立多行字符串。     

    val s1 = """This is
        a multiline
        String
      """
    println(s1)

  3. 讨论

  使用上述方法能够建立多行字符串,可是当打印时,其结果以下 

This is
        a multiline
        String

  第二行和第三行是以空格开头,若须要使第二行、第三行的字符串都不以空格开头,则可进行以下处理。    

复制代码
    val s1 =
      """
        |This
        |is a multiline
        |String
      """.stripMargin
    
    println(s1)
复制代码

  结果以下

This
is a multiline
String

  即经过|来处理,或者使用其余符号,如#,同时使用stripMargin方法。 

    val s1 = """This is
        #a multiline
        #String
      """.stripMargin('#')
    println(s1)

  结果以下

This is
a multiline
String

  上述例子中,在第一行is和第二行multiline后面均隐藏了\n字符进行换行,当须要将多行字符串合并为一行时,能够在使用stripMargin方法以后使用replaceAll方法来将全部的\n替换为" "。

  

  另外,在三引号字符串中能够包含特殊字符,而并不须要转义符进行转义。

     

  2.3 分割字符串

  1. 问题描述

  你须要使用分割字符来分割字符串,如从逗号分隔(CSV)或管道分隔的文件中获取字符串。

  2. 解决方案

  可使用String的split方法进行分割

  

  split方法会返回String数组。

  3. 讨论

  split函数的参数能够是正则表达式,因此对于CSV文件,你可使用逗号进行分割字符串。

  

  能够看到,经过","进行分割时,结果中还包含了一些空格,如" milk", " butter", " Coco Puffs",此时,须要使用trim函数来去掉空格。

  

  咱们也可使用正则表达式来分割字符串

  

  split方法是重载的,一部分从Java的String而来,一部分从Scala的StringLike而来,例如,你可使用字符而非字符串做为参数来调用split,此时,你使用的是StringLike的方法

  

  此时,使用字符和字符串做为参数二者的结果是相同的。

  2.4 将变量替换成字符串

  1. 问题描述

  如同Perl、PHP、Ruby同样,你须要将变量替换成字符串。

  2. 解决方案

  为在Scala中使用字符串插值,字符串前面须要使用字母s,同时须要将变量包含在字符串中,而每一个变量名前面是一个$字符。

  

  当在字符串前面使用字母s时,表示正在建立一个已处理的字符串字面量,便可以在字符串中直接使用变量。

  字符串字面量中使用表达式

  除了在字符串中使用变量外,一样也能够在字符串中使用表达式,此时表达式须要放在中括号内。

  

  

  也可使用中括号打印对象的属性。

  

  s是一个方法

  放在字符串字面量前面的s其实是一个方法,而使用s方法可让你享受以下便利

    · Scala提供了其余现成的插值函数

    · 你可自定义字符串插值函数

  f字符串插值(printf格式)

  在讨论中提到,weight被打印成65,可是若是须要在weight后面增长多位小数点应该怎么作,可使用f字符串插值方法,其能够格式化字符串中的说明符。 

  

  为使用f字符串插值,首先须要在字符串前面添加f,而后在变量后面使用printf格式化说明符。

  粗插值

  除了使用s和f插值方法外,Scala还包括了粗插值方法,其会保留字符串中的特殊字符。

  

  能够看到使用raw修饰字符串时,其会保留字符串中的特殊字符。

  下表列出了最经常使用的说明符

  

  2.5 一次处理字符串的一个字符

  1. 问题描述

  你须要遍历字符串的每一个字符,而且对每一个字符作相应的操做。

  2. 解决方案

  可使用map方法、foreach方法、for循环等方法来遍历字符串。

  

  或者使用下划线的方式

  

  对于字符串的字符序列,你可使用链式调用来获得想要的结果,在下面的示例中,filter方法用于原始的字符串来生成新的字符串(去掉全部的字符l),而后再调用map方法将新生成的字符串转化为大写。

  

  使用for循环和yield也能够达到map方法的效果

  

  map方法、for和yield方法能够将旧的集合转化为新的集合,而foreach方法则是对集合的每一个元素进行操做,不会产生新的结果。

  3. 讨论

  因为Scala将字符串当成是字符序列,而Scala也是面向对象和函数编程语言,在Java中,你可使用以下方法来遍历字符串中的每一个字符  

String s = "hello"
for (int i = 0; i < s.length(); i++) {
    char c = s.charAt(i);
    System.out.println(c);    
}

  理解map方法的工做机制

  在map方法中,你能够传入一大段代码块

  

  上述功能是将字符串的字符由大写变换成小写,因为是调用的String的map方法,所以每次只会处理字符串的一个字符,map会将String当成一个字符顺序集合,map方法有一个隐式循环,在此循环中,每次只会传入一个字符。除了在map方法中直接传入代码块外,也能够先定义好函数,而后再传入map,这样能够保证代码的简洁性。

  

  而且也能够在for循环和yield中使用该方法

  

  除了使用方法方式外,还可使用函数方式来完成上述的操做

  

  2.6 在字符串中查找模式

  1. 问题描述

  你须要肯定字符串是否包含正则表达式模式。

  2. 解决方案

  经过String的.r方法来建立一个Regex对象,以后当查找第一个匹配时,使用findFirstIn方法,而查找全部匹配时,使用findAllIn方法。

  

  对于findAllIn方法而言,能够将结果转化成Array、List、Seq等

  

  3. 讨论

  使用字符串的.r方法是建立Regex对象最简单的方式,另一种方式是导入Regex类,建立一个Regex实例,以后使用实例的方法

  

  处理findFirstIn返回的结果

  findFirstIn查找第一个匹配而且返回一个Option[String]

  Option/Some/None类型将会在以后的章节进行讨论,能够简单的认为Option是一个容器,它要么持有0或者一个值,对于findFirstIn,成功时会返回Some("123"),不成功时会返回None

  

  返回类型为Option[String]的方法要么返回Some(String),要么返回None

  针对Option类型,想要获取其值,可使用以下方法

    · getOrElse

    · 使用foreach

    · 使用匹配表达式 

  使用getOrElse方法,你能够尝试获取值,或者失败时定义缺省值

  

  使用foreach方法以下

  

  使用匹配表达式方法以下

  

  2.7 字符串的替换模式

  1. 问题描述

  你须要要在字符串中搜索正则表达式模式,并替换它们。

  2. 解决方案

  因为String是不可变的,因此你不能直接在字符串上进行查找并替换的操做,可是你能够建立包含替换内容的新字符串,可使用replaceAll方法,要记得将结果赋值

  

  也可建立一个正则表达式,而后调用replaceAllIn方法,一样要记得将结果赋值给新的字符串

  

  也能够调用replaceFirstIn来替换第一个匹配值,一样要记得将结果赋值给新的字符串

  

  2.8 提取模式匹配的字符串部分

  1. 问题描述

  你须要提取字符串的一个或多个匹配正则达表达式的部分。

  2. 解决方案

  首先定义提取的正则表达式模式,而后将它们放置在括号里面造成正则表达式组

  

  上述示例从指定字符串中提取了数字部分和字母部分,而且分别赋值给count和fruit。

  3. 讨论

  上述示例的语法可能有点古怪,彷佛是将模式两次定义成val字段,可是这种语法很便捷而且可读性很高,能够试想你在编写一个搜索引擎,你想让人们用各类各样的短语搜索电影,你可让他们输入任何这些短语来得到电影列表

  

  你能够定义一系列的正则表达式进行匹配,例如 

// match "movies 80301"
val MoviesZipRE = "movies (\\d{5})".r

// match "movies near boulder, co"
val MoviesNearCityStateRE = "movies near ([a-z]+), ([a-z]{2})".r

  以后能够根据用户的输入进行匹配,而后获取搜索结果,伪代码以下 

textUserTyped match {
case MoviesZipRE(zip) => getSearchResults(zip)
case MoviesNearCityStateRE(city, state) => getSearchResults(city, state)
case _ => println("did not match a regex")
}

  上述的正则表达式能够匹配以下字符串

  

  在匹配时,须要考虑全部状况,如case _ 表示不能匹配,以下字符串将没法匹配

  

  2.9 访问字符串的字符

  1. 问题描述

  你想要获取字符串中特定位置的字符。

  2. 解决方案

  可使用Java的charAt方法

  

  另外,一种更好的方法是数组表示法

  

  3. 讨论

  当map方法和foreach不适用时,能够将String看作Array类型,而后使用数组表示法来访问字符,Scala中的数组表示法不一样于Java的数组表示法,由于在Scala中,数组表示法是一个方法调用。

  

  在调用数组表示法时实际上调用的是apply方法,,可是因为Scala语法糖的存在,能够直接使用数组表示法获取指定字符。

  2.10 向String类添加自定义方法

  1. 问题描述

   你想要向字符串类添加自定义方法,如"HAL".increment,而非使用工具类StringUtilities的increment方法。

  2. 解决方案

  能够定义一个隐式类,而后在类中定义你想要添加的方法

  

  在实际的编码中会稍微复杂一点,由于隐式类须要必需要被定义在能够被定义方法的做用域中,这意味着隐式类必须定义在class、object、package object中。 

复制代码
package com.leesf.utils

object StringUtils {
    implicit class StringImprovements(val s: String) {
        def increment = s.map(c => (c + 1).toChar)
    }
}    
复制代码

  在使用时,须要导入com.leesf.utils.StringUtils

复制代码
package foo.bar

import com.leesf.utils.StringUtils._

object Main extends App {
    println("HAL".increment)
}
复制代码

  还可将隐式类放在包对象中 

复制代码
package com.leesf

package object utils {
    implicit class StringImprovements(val s: String) {
        def increment = s.map(c => (c + 1).toChar)
    }
}    
复制代码

  在使用时,须要导入com.leesf.utils  

复制代码
package foo.bar

import com.leesf.utils._

object MainDriver extends App {
    println("HAL".increment)
}
复制代码

  针对Scala2.10前的版本,与上述作法有稍许不一样,首先须要在普通的类中定义increment方法  

class StringImprovements(val s: String) {
    def increment = s.map(c => (c + 1).toChar)
}

  而后定义隐式方法进行转化  

implicit def stringToString(s: String) = new StringImprovements(s)

  3. 讨论

  在Scala中,你能够经过隐式转化向封闭的类添加新的方法,而且在使用时导入,而不须要继承该类来添加方法(有些final类根本不能继承)。

3、总结

  本篇博文讲解了Scala中的String知识点,其也是在Scala编程中使用频率很是高的知识点,对其进行了梳理,加深了印象,也谢谢各位园友的观看~ 

相关文章
相关标签/搜索