Google Guava之字符串处理
Google Guava依赖
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1.1-jre</version>
</dependency>
连接器[Joiner]
连接器工厂
方法 | 描述 | 范例 |
---|---|---|
Joiner.on(char) | 按单个字符连接 | Joiner.on(’,’) |
Joiner.on(CharMatcher) | 按字符匹配器连接 | Joiner.on(",") |
连接器修饰符
方法 | 描述 |
---|---|
skipNulls() | 过滤字符串序列中的null值 |
useForNull(String) | 替换字符串序列中的null值 |
join(Iterable) | 连接对象类型的数据 |
用分隔符把字符串序列连接起来也可能会遇上不必要的麻烦。如果字符串序列中含有null
,那连接操作会更难。Fluent风格的Joiner
符串更简单。
String joinerStr = Joiner.on(";").skipNulls().join("Harry", null, "Ron", "Hermione");
//Harry;Ron;Hermione
另外,useForNull(String)
方法可以给定某个字符串来替换null
,而不像skipNulls()
方法是直接忽略null
。 Joiner
也可以用来连接对象类型,在这种情况下,它会把对象的toString()
值连接起来。
String joinerStr = Joiner.on(";").useForNull("aries").join("Harry", null, "Ron", "Hermione");
//Harry;aries;Ron;Hermione
//User中包含age和name两个属性
User user1 = User.builder().age(18).name("张三").build();
User user2 = User.builder().age(19).name("李四").build();
String join = Joiner.on(";").join(Arrays.asList(user1, user2));
//User(age=18, name=张三);User(age=19, name=李四)
警告:joiner实例总是不可变的。用来定义joiner目标语义的配置方法总会返回一个新的joiner实例。这使得joiner实例都是线程安全的,你可以将其定义为static final常量。
拆分器[Splitter]
JDK内建的字符串拆分工具有一些古怪的特性。比如,String
的split()
方法悄悄丢弃了尾部的分隔符。
Splitter
使用令人放心的、直白的流式API模式对这些混乱的特性做了完全的掌控。
拆分器工厂
方法 | 描述 | 范例 |
---|---|---|
Splitter.on(char) | 按单个字符拆分 | Splitter.on(‘;’) |
Splitter.on(CharMatcher) | 按字符匹配器拆分 | Splitter.on(CharMatcher.breakingWhitespace()) |
Splitter.on(String) | 按字符串拆分 | Splitter.on(“, “) |
Splitter.on(Pattern) Splitter.onPattern(String) | 按正则表达式拆分 | Splitter.onPattern(“\r\n”) |
Splitter.fixedLength(int) | 按固定长度拆分;最后一段可能比给定长度短,但不会为空 | Splitter.fixedLength(3) |
拆分器修饰符
方法 | 描述 |
---|---|
omitEmptyStrings() | 从结果中自动忽略空字符串 |
trimResults() | 移除结果字符串的前导空白和尾部空白 |
trimResults(CharMatcher) | 给定匹配器,移除结果字符串的前导匹配字符和尾部匹配字符 |
limit(int) | 限制拆分出的字符串数量 |
split(final CharSequence) | 拆分结果为Iterable |
splitToList(CharSequence) | 拆分结果为List |
List<String> splitList = Splitter.on(',')
.trimResults()
.omitEmptyStrings()
.splitToList("apple,watermelon,, banana");
//[apple, watermelon, banana]
注:splitter实例总是不可变的。用来定义splitter目标语义的配置方法总会返回一个新的splitter实例,这使得splitter实例都是线程安全的,你可以将其定义为static final常量。
字符匹配器[CharMatcher]
在以前的Guava版本中,StringUtil类疯狂地膨胀,其拥有很多处理字符串的方法:allAscii、collapse、collapseControlChars、collapseWhitespace、indexOfChars、lastIndexNotOf、numSharedChars、removeChars、removeCrLf、replaceChars、retainAllChars、strip、stripAndCollapse、stripNonDigits。 所有这些方法指向两个概念上的问题:
- 怎么才算匹配字符?
- 如何处理这些匹配字符?
为了收拾这个泥潭,我们开发了CharMatcher。
直观上,你可以认为一个CharMatcher实例代表着某一类字符,如数字或空白字符。事实上来说,CharMatcher实例就是对字符的布尔判断—CharMatcher确实也实现了Predicate—但类似”所有空白字符”或”所有小写字母”的需求太普遍了,Guava因此创建了这一API。
然而使用CharMatcher的好处更在于它提供了一系列方法,让你对字符作特定类型的操作:修剪[trim]、折叠[collapse]、移除[remove]、保留[retain]等等。这样的设计使得API复杂度的线性增加可以带来灵活性和功能两方面的增长。
获取字符匹配器
获取字符匹配器的常见方法包括:
方法 | 描述 |
---|---|
any() | 匹配任何字符 |
none() | 不匹配所有字符 |
whitespace() | 匹配所有空白字符 |
breakingWhitespace() | 匹配所有可换行的空白字符(不包括非换行空白字符,例如"\u00a0") |
invisible() | 匹配所有看不见的字符 |
digit() | 匹配ASCII数字 |
javaLetter() | 匹配字母 |
javaDigit() | 匹配UNICODE数字 |
javaLetterOrDigit() | 匹配数字或字母 |
javaIsoControl() | 匹配ISO控制字符(制表符、回车、换行等) |
javaLowerCase() | 匹配小写 |
javaUpperCase() | 匹配大写 |
ascii() | 匹配ASCII字符 |
singleWidth() | 匹配单字宽字符, 如中文字就是双字宽 |
anyOf(CharSequence) | 枚举匹配字符。如CharMatcher.anyOf(“aeiou”)匹配小写英语元音 |
is(char) | 给定单一字符匹配 |
inRange(char, char) | 给定字符范围匹配,如CharMatcher.inRange(‘a’,‘z’) |
negate() | 返回以当前Matcher判断规则相反的Matcher |
and(CharMatcher) | 返回与其他匹配条件组合来做与判断的Matcher |
or(CharMatcher) | 返回与其他匹配条件组合来做或判断的Matcher |
使用字符匹配器
CharMatcher提供了多种多样的方法操作CharSequence中的特定字符。其中最常用的罗列如下:
方法 | 描述 |
---|---|
collapseFrom(CharSequence,char) | 把每组连续的匹配字符替换为特定字符。如whitespace().collapseFrom(str,';') 把字符串中的连续空白字符替换为";" |
matchesAllOf(CharSequence) | 测试是否字符序列中的所有字符都匹配 |
removeFrom(CharSequence) | 从字符序列中移除所有匹配字符 |
retainFrom(CharSequence) | 在字符序列中保留匹配字符,移除其他字符 |
trimFrom(CharSequence) | 移除字符序列的前导匹配字符和尾部匹配字符 |
replaceFrom(CharSequence,CharSequence) | 用特定字符序列替代匹配字符 |
trimAndCollapseFrom(CharSequence,char) | 去除两端的空格,并把中间连续的匹配字符替换为特定字符 |
所有这些方法返回String
,除了matchesAllOf()
返回的是boolean
。
String str = "abc\td123 ABC";
//移除control字符
String noControl = CharMatcher.javaIsoControl().removeFrom(str);
//abcd123 ABC
//只保留数字字符
String theDigits = CharMatcher.digit().retainFrom(str);
//123
//去除两端的空格,并把中间的连续空格替换成";"(\t也会被当做空格)
String spaced = CharMatcher.whitespace().trimAndCollapseFrom(str, ';');
//abc;d123;ABC
//用*号替换所有数字
String noDigits = CharMatcher.javaDigit().replaceFrom(str, "*");
//abc d*** ABC
// 只保留数字和小写字母
String lowerAndDigit = CharMatcher.javaDigit().or(CharMatcher.javaLowerCase()).retainFrom(str);
//abcd123
注:CharMatcher只处理char类型代表的字符;它不能理解0x10000到0x10FFFF的Unicode 增补字符。这些逻辑字符以代理对[surrogate pairs]的形式编码字符串,而CharMatcher只能将这种逻辑字符看待成两个独立的字符。
字符集[Charsets]
不要这样做字符集处理:
String str = "abc";
try {
byte[] bytes = str.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new AssertionError(e);
}
试试这样写:
byte[] bytes = string.getBytes(Charsets.UTF_8);
Charsets
针对所有Java平台都要保证支持的六种字符集提供了常量引用。尝试使用这些常量,而不是通过名称获取字符集实例。
大小写格式[CaseFormat]
CaseFormat
被用来方便地在各种ASCII大小写规范间转换字符串—比如,编程语言的命名规范。CaseFormat
支持的格式如下:
格式 | 描述 | 范例 |
---|---|---|
LOWER_CAMEL | Java变量的命名规则,如“lowerCamel” | lowerCamel |
LOWER_HYPHEN | 连字符连接变量的命名规则,如“lower-hyphen” | lower-hyphen |
LOWER_UNDERSCORE | C ++变量命名规则,如“lower_underscore” | lower_underscore |
UPPER_CAMEL | Java和C++类的命名规则,如“UpperCamel” | UpperCamel |
UPPER_UNDERSCORE | Java和C++常量的命名规则,如“UPPER_UNDERSCORE” | UPPER_UNDERSCORE |
CaseFormat的用法很直接:
String to(CaseFormat format, String str):从这一格式指定格式的指定字符串str转换
//常量命名规则转换小驼峰命名规则
CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, "CONSTANT_NAME");
//constantName
我们CaseFormat
在某些时候尤其有用,比如编写代码生成器的时候。