Scala的String就是java.lang.String但Scala给它装上了强大的扩展方法。插值器、多行字符串、集合操作化——让字符串处理从Java的繁琐中解放出来。如果你从Java转到Scala处理字符串时可能会有两个疑问Scala的String和Java的String是同一个东西吗为什么可以直接写sHello, $name第一个问题的答案是是的Scala的String就是java.lang.String编译后完全一样。第二个问题的答案是Scala通过隐式转换和扩展方法给String穿上了一层更强大的外衣——你写的是String用的却是更丰富的API。在大数据开发中字符串处理无处不在日志解析、ETL清洗、JSON序列化、SQL拼接、Kafka消息处理。Spark的DataFrame列操作、Flink的数据流转换、AI管道中的文本预处理——都离不开字符串操作。掌握Scala的字符串处理就是掌握了大数据工程的基本功。这篇文章从七个层面拆解Scala的String操作帮你从Java的字符串处理中平滑迁移。01 字符串基础不可变的Unicode序列String就是java.lang.String// Scala的String就是Java的Stringvals:Stringhello// 编译后完全等价于Java的java.lang.String// 可以直接调用Java的方法s.length// 5s.toUpperCase// HELLOs.substring(0,3)// hel// 也可以用Scala特有的方式s.head// hs.last// os.tail// ellos.reverse// olleh字符串是不可变的和Java一样Scala的String也是不可变的valshello// s(0) H // 编译错误String是不可变的// 所有修改操作都返回新字符串valuppers.toUpperCase// HELLO新字符串valreplaceds.replace(h,H)// Hello新字符串// s本身没变println(s)// hello字符串的是值比较// Java: 是引用比较// String a new String(hello);// String b new String(hello);// a b // false引用不同// Scala: 是值比较调用equalsvalanewString(hello)valbnewString(hello)ab// true值相等a eq b// false引用不等// 这意味着Scala中比较字符串内容直接用不需要equals对Java开发者的启示Java里比较字符串内容要写a.equals(b)容易忘记而写出bug。Scala里就是值比较eq才是引用比较——这种设计让正确的写法成为默认。02 字符串插值器s、f、raw字符串插值器是Scala最实用的语法特性之一彻底替代了Java的拼接和String.format。s插值器最常用valnameScalavalversion3// s插值器直接插入变量println(sHello,$name! Version is$version)// Hello, Scala! Version is 3// ${}内可以写表达式println(s1 1 ${11})// 1 1 2// ${}内可以访问对象字段caseclassUser(name:String,age:Int)valuserUser(Alice,25)println(sUser:${user.name}, age:${user.age})// User: Alice, age: 25// 嵌套使用println(sResult:${if(score60)PASSelseFAIL})f插值器格式化字符串f插值器类似Java的String.format但更简洁valpi3.14159265// 保留2位小数println(fPi is approximately$pi%.2f)// Pi is approximately 3.14// 整数补零valid42println(fID:$id%05d)// ID: 00042// 百分比valratio0.8567println(f成功率:$ratio%.2f%%)// 成功率: 85.67%// 左对齐/右对齐valnameAliceprintln(f|$name%10s|)// | Alice|println(f|$name%-10s|)// |Alice |raw插值器不转义// s插值器会转义特殊字符println(sHello\nWorld)// Hello// World// raw插值器不转义原样输出println(rawHello\nWorld)// Hello\nWorld// 用于正则表达式和文件路径valregexraw\d{3}-\d{4}valpathrawC:\Users\name\file.txt自定义插值器Scala允许自定义插值器这是一个强大的扩展能力// 自定义日期插值器objectdate{defapply(sc:StringContext):String???defunapply(sc:StringContext):String???}// 实际开发中更常用的是内置插值器// s、f、raw已经覆盖99%的场景03 多行字符串与stripMarginJava里写多行字符串需要用拼接非常繁琐。Scala用三引号字符串解决// 三引号字符串可以跨多行valjson{ name: Scala, version: 3, features: [插值器, 模式匹配, 高阶函数] }// 多行SQLvalsql SELECT id, name, age FROM users WHERE age 18 ORDER BY age DESC // 三引号内可以直接写引号不需要转义valquoteHe said Scala is great.stripMargin去掉左边距多行字符串通常有缩进用stripMargin去掉valtext |这是一段 |多行字符串 |用 | 作为边距 |.stripMargin// 结果:// 这是一段// 多行字符串// 用 | 作为边距// 默认边距字符是|也可以自定义valtext2 # 标题 # - 列表项1 # - 列表项2 .stripMargin(#)strip.stripSuffix去掉首尾空白valmessy | hello world | .stripMargin.trim// hello world// 去掉特定前缀/后缀hello.txt.stripSuffix(.txt)// helloprefix_hello.stripPrefix(prefix_)// hello04 常用字符串操作拼接与比较// 拼接valaHellovalbWorlda b// Hello Worlds$a$b// Hello World// 多个字符串拼接List(a,b,c).mkString(-)// a-b-cList(a,b,c).mkString// abcList(a,b,c).mkString([,, ,])// [a, b, c]// 比较abcabc// trueabc.equalsIgnoreCase(ABC)// trueabc.compareTo(abd)// -1查找与匹配valshello world// 包含s.contains(world)// trues.contains(scala)// false// 前缀/后缀s.startsWith(hello)// trues.endsWith(world)// true// 查找位置s.indexOf(o)// 4s.indexOf(o,5)// 7从位置5开始找s.lastIndexOf(o)// 7// 查找多个位置hello world hello.indexOfSlice(hello)// 0替换与分割valshello,scala,world// 替换单个字符s.replace(,,-)// hello-scala-world// 替换子串s.replace(scala,Scala)// hello,Scala,world// 正则替换a1b2c3.replaceAll([0-9],)// abc// 分割s.split(,)// Array(hello, scala, world)a-b-c.split(-)// Array(a, b, c)// 按正则分割a1b2c3.split(\\d)// Array(a, b, c)大小写与修剪vals Hello World s.trim// Hello Worlds.toUpperCase// HELLO WORLD s.toLowerCase// hello world s.capitalize// hello world 首字母大写// 大小写不敏感的包含Hello.regionMatches(true,0,hello,0,5)// true子串与字符访问valshello// 取子串s.substring(1)// ellos.substring(1,4)// ell// 字符访问类似数组s(0)// hs(4)// o// 字符遍历for(c-s)println(c)// 逐个打印字符// 字符串切片s.slice(1,4)// ell05 字符串与集合函数式操作Scala的String可以被当作字符的集合来操作——这是Java做不到的。String就是Seq[Char]valshello// String隐式转换为WrappedStringSeq[Char]s.length// 5s(0)// hs.head// hs.last// os.tail// ellos.reverse// ollehs.take(3)// hels.drop(3)// lomap/filter/flatMapvalsHello World// map转换每个字符s.map(_.toUpper)// HELLO WORLDs.map(cif(c )-elsec)// Hello-World// filter过滤字符s.filter(_.isUpper)// HWs.filter(_! )// HelloWorld// flatMap每个字符生成多个字符s.flatMap(cc.toString*2)// HHeelllloo WWoorrlldd// 组合使用Hello123World.filter(_.isLetter).map(_.toLower)// helloworld字符串转换为集合valshello// 转换为字符数组s.toCharArray// Array(h,e,l,l,o)// 转换为Lists.toList// List(h,e,l,l,o)// 转换为Set去重s.toSet// Set(h,e,l,o)// 按字符分组s.groupBy(identity).view.mapValues(_.length).toMap// Map(h - 1, e - 1, l - 2, o - 1)集合转换为字符串// List转字符串List(apple,banana,orange).mkString(, )// apple, banana, orange// 带前缀后缀List(1,2,3).mkString([,, ,])// [1, 2, 3]// 字符列表转字符串List(a,b,c).mkString// abc大数据场景提示Spark的DataFrame中字符串列的处理大量使用这些操作。split、concat_ws、regexp_replace、upper/lower——这些Spark SQL函数的底层实现本质上就是Scala字符串操作的分布式版本。06 正则表达式Scala的正则表达式比Java更简洁创建正则// 用.r方法创建正则valemailPattern[a-zA-Z0-9._%-][a-zA-Z0-9.-]\\.[a-zA-Z]{2,}.r// 更清晰的写法用raw字符串valphonePatternraw\d{3}-\d{4}-\d{4}.r// 命名捕获组valdatePatternraw(\d{4})-(\d{2})-(\d{2}).r匹配与提取valemailuserexample.com// 匹配检查emailPattern.matches(email)// true// 查找第一个匹配datePattern.findFirstMatchIn(2024-01-15)match{caseSome(m)println(s年:${m.group(1)})// 2024caseNoneprintln(不匹配)}// 提取捕获组valdatePattern(year,month,day)2024-01-15// year 2024, month 01, day 15// 查找所有匹配valtext订单123和订单456valorderPatternraw订单(\d).r orderPattern.findAllMatchIn(text).map(_.group(1)).toList// List(123, 456)替换valtext我的电话是138-1234-5678备用电话是139-8765-4321// 替换所有匹配phonePattern.replaceAllIn(text,***)// 我的电话是***备用电话是***// 替换第一个匹配phonePattern.replaceFirstIn(text,138-****-****)// 正则分割a1b2c3.split(raw\d.r.regex)// Array(a, b, c)07 与Java字符串操作的对比理解Scala和Java字符串处理的差异能帮助你避免常见的迁移陷阱。核心差异对比维度JavaScala字符串拼接 或 StringBuilder插值器s…格式化String.formatf…多行字符串 拼接或文本块“”“三引号”“”值比较equals() 直接比较字符操作需转char数组隐式转Seq[Char]正则Pattern.compile.r方法集合操作需转流直接map/filter分割后去空需手动处理split.toList.filter(_.nonEmpty)Java拼接 vs Scala插值器// Java: 拼接字符串StringnameAlice;intage25;StringmessageHello, name! You are age years old.;// Java: 格式化StringformattedString.format(Hello, %s! You are %d years old.,name,age);// Scala: 插值器valnameAlicevalage25valmessagesHello,$name! You are$ageyears old.// Scala: 格式化valformattedfHello,$name! You are$ageyears old.Java正则 vs Scala正则// Java: 正则表达式PatternpatternPattern.compile((\\d{4})-(\\d{2})-(\\d{2}));Matchermatcherpattern.matcher(2024-01-15);if(matcher.find()){Stringyearmatcher.group(1);Stringmonthmatcher.group(2);Stringdaymatcher.group(3);}// Scala: 正则表达式valdatePatternraw(\d{4})-(\d{2})-(\d{2}).rvaldatePattern(year,month,day)2024-01-15// year 2024, month 01, day 15Scala字符串工具类Scala标准库还有一些实用的字符串工具// StringLike trait提供的扩展方法importscala.collection.immutable.StringOps// 缩进hello.indent(2)// hello// 对齐hello.padTo(10, )// hello hello.padTo(10,-)// hello-----// 重复abc*3// abcabcabc// 字符计数hello.count(_l)// 2写在最后字符串处理是数据工程的基本功Scala的字符串操作表面上是给Java String加了语法糖本质上是把字符串从一个不可变的字符序列提升为一个可以函数式操作的集合。这种提升带来三个好处更简洁的拼接插值器替代了Java的拼接和String.format代码更易读更强大的操作map/filter/flatMap让字符串处理像集合操作一样组合更安全的比较就是值比较消除了Java中和equals的混淆这些特性不是孤立的它们共同支撑了Scala的函数式编程范式。Spark的split函数、Flink的字符串处理、AI管道的文本预处理——这些框架的底层都是Scala字符串操作的分布式版本。学习建议不要试图一次记住所有方法。先从插值器和常用操作trim/split/replace开始写一些日志解析的小练习。当你习惯了用map/filter处理字符串后再学习正则表达式和多行字符串。字符串处理是编程的基本功但在Scala里它不仅是基本功——它是函数式思维的训练场。从Java的字符串拼接和分割到Scala的插值器和集合操作不是学习另一种字符串API而是学习一种更函数式、更声明式、更组合的文本处理方式。当你习惯了用s插值器拼接、用split分割、用map/filter清洗你会发现原来字符串处理可以这么优雅。
阅读完成 · 觉得有帮助?