2023-08-17
原文作者:LifeIsForSharing 原文地址:https://solang.blog.csdn.net/article/details/105101316

使用示例时须导入以下jar包进行测试,guava版本使用28.2-jre

    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>28.2-jre</version>
    </dependency>
    
    <dependency>
        <groupId>com.google.truth</groupId>
        <artifactId>truth</artifactId>
        <version>1.0.1</version>
        <scope>compile</scope>
    </dependency>
    
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava-testlib</artifactId>
        <version>28.2-jre</version>
        <scope>compile</scope>
    </dependency>
    
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13</version>
        <scope>compile</scope>
    </dependency>

Strings:一些非常有用的字符串工具:拆分,连接,填充等。

1.Ascii字符

与ASCII字符(值在0x000x7F范围内的字符)有关的静态方法,以及包含此类字符的字符串。

1.1判断是否小写

方法:boolean isLowerCase(char c)

指示c是否为’a’和’z’之间的26个小写ASCII字母字符之一。所有其他字符(包括非ASCII字符)都返回false。

1.2判断是否大写

方法:boolean isUpperCase(char c)

指示c是否为’A’和’Z’之间的26个小写ASCII字母字符之一。所有其他字符(包括非ASCII字符)都返回false。

1.3返回小写

方法:char toLowerCase(char c)

如果参数为isUpperCase(char)大写ASCII字符,则返回等效的小写字母。否则返回参数。

方法:String toLowerCase(CharSequence chars)

返回输入字符序列的副本,其中所有isUpperCase(char)大写ASCII字符都已转换为小写。所有其他字符均被复制而没有修改。

方法:String toLowerCase(String string)

返回输入字符串的副本,其中所有isUpperCase(char)大写ASCII字符都已转换为小写。所有其他字符均被复制而没有修改。

1.4返回大写

参见上一节

1.5忽略大小写判断

方法:boolean equalsIgnoreCase(CharSequence s1, CharSequence s2)

指示给定字符序列s1s2的内容是否相等,而忽略'a''z''A''Z'(含)之间的任何ASCII字母字符的情况。
此方法比String#equalsIgnoreCase明显快得多,如果已知至少一个参数仅包含ASCII字符,则应优先使用此方法。
但是请注意,此方法的行为并不总是与以下表达式相同:

    string.toUpperCase().equals("UPPER CASE ASCII")
    string.toLowerCase().equals("lower case ascii")
    

由于某些非ASCII字符的大小写折叠(在String#equalsIgnoreCase中不会发生)。但是,在几乎所有使用ASCII字符串的情况下,作者都可能希望此方法提供的行为,而不是toUpperCase()toLowerCase()的微妙且有时令人惊讶的行为。

1.6截断

方法:String truncate(CharSequence seq, int maxLength, String truncationIndicator)

将给定的字符序列截断为给定的最大长度。如果序列的长度大于maxLength,则返回的字符串> 的长度将精确为maxLength个字符,并以给定的truncationIndicator结尾。否则,序> > 列将以字符串形式返回,且内容不变。

示例:

    Ascii.truncate("foobar", 7, "..."); // returns "foobar"
    Ascii.truncate("foobar", 5, "..."); // returns "fo..."
    

注意: 此方法可以用于某些非ASCII文本,但与任意Unicode文本一起使用不安全。它主要用于与已知可以安全使用的文本(例如,全ASCII文本)和简单的调试文本一起使用。使用此方法时,请考虑以下事项:

  • 它可能会拆分代理对
  • 它可以分割字符并组合字符
  • 它不考虑单词边界
  • 如果要截断以显示给用户,则必须考虑其他因素
  • 适当的截断指示符可能取决于语言环境
  • 在截断指示器中使用非ASCII字符是安全的

如果maxLength小于truncationIndicator的长度,则throws IllegalArgumentException

1.7使用示例

    import com.google.common.annotations.GwtIncompatible;
    import com.google.common.base.Ascii;
    import junit.framework.TestCase;
    
    public class AsciiTest extends TestCase {
    
        /**
         * The Unicode points {@code 00c1} and {@code 00e1} are the upper- and lowercase forms of
         * A-with-acute-accent, {@code Á} and {@code á}.
         */
        private static final String IGNORED = "`10-=~!@#$%^&*()_+[]\\{}|;':\",./<>?'\u00c1\u00e1\n";
    
        private static final String LOWER = "abcdefghijklmnopqrstuvwxyz";
        private static final String UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    
        public void testToLowerCase() {
            assertEquals(LOWER, Ascii.toLowerCase(UPPER));
            assertSame(LOWER, Ascii.toLowerCase(LOWER));
            assertEquals(IGNORED, Ascii.toLowerCase(IGNORED));
            assertEquals("foobar", Ascii.toLowerCase("fOobaR"));
        }
    
        public void testToUpperCase() {
            assertEquals(UPPER, Ascii.toUpperCase(LOWER));
            assertSame(UPPER, Ascii.toUpperCase(UPPER));
            assertEquals(IGNORED, Ascii.toUpperCase(IGNORED));
            assertEquals("FOOBAR", Ascii.toUpperCase("FoOBAr"));
        }
    
        public void testCharsIgnored() {
            for (char c : IGNORED.toCharArray()) {
                String str = String.valueOf(c);
                assertTrue(str, c == Ascii.toLowerCase(c));
                assertTrue(str, c == Ascii.toUpperCase(c));
                assertFalse(str, Ascii.isLowerCase(c));
                assertFalse(str, Ascii.isUpperCase(c));
            }
        }
    
        public void testCharsLower() {
            for (char c : LOWER.toCharArray()) {
                String str = String.valueOf(c);
                assertTrue(str, c == Ascii.toLowerCase(c));
                assertFalse(str, c == Ascii.toUpperCase(c));
                assertTrue(str, Ascii.isLowerCase(c));
                assertFalse(str, Ascii.isUpperCase(c));
            }
        }
    
        public void testCharsUpper() {
            for (char c : UPPER.toCharArray()) {
                String str = String.valueOf(c);
                assertFalse(str, c == Ascii.toLowerCase(c));
                assertTrue(str, c == Ascii.toUpperCase(c));
                assertFalse(str, Ascii.isLowerCase(c));
                assertTrue(str, Ascii.isUpperCase(c));
            }
        }
    
        public void testTruncate() {
            assertEquals("foobar", Ascii.truncate("foobar", 10, "..."));
            assertEquals("fo...", Ascii.truncate("foobar", 5, "..."));
            assertEquals("foobar", Ascii.truncate("foobar", 6, "..."));
            assertEquals("...", Ascii.truncate("foobar", 3, "..."));
            assertEquals("foobar", Ascii.truncate("foobar", 10, "…"));
            assertEquals("foo…", Ascii.truncate("foobar", 4, "…"));
            assertEquals("fo--", Ascii.truncate("foobar", 4, "--"));
            assertEquals("foobar", Ascii.truncate("foobar", 6, "…"));
            assertEquals("foob…", Ascii.truncate("foobar", 5, "…"));
            assertEquals("foo", Ascii.truncate("foobar", 3, ""));
            assertEquals("", Ascii.truncate("", 5, ""));
            assertEquals("", Ascii.truncate("", 5, "..."));
            assertEquals("", Ascii.truncate("", 0, ""));
        }
    
        public void testTruncateIllegalArguments() {
            String truncated = null;
            try {
                truncated = Ascii.truncate("foobar", 2, "...");
                fail();
            } catch (IllegalArgumentException expected) {
            }
    
            try {
                truncated = Ascii.truncate("foobar", 8, "1234567890");
                fail();
            } catch (IllegalArgumentException expected) {
            }
    
            try {
                truncated = Ascii.truncate("foobar", -1, "...");
                fail();
            } catch (IllegalArgumentException expected) {
            }
    
            try {
                truncated = Ascii.truncate("foobar", -1, "");
                fail();
            } catch (IllegalArgumentException expected) {
            }
        }
    
        public void testEqualsIgnoreCase() {
            assertTrue(Ascii.equalsIgnoreCase("", ""));
            assertFalse(Ascii.equalsIgnoreCase("", "x"));
            assertFalse(Ascii.equalsIgnoreCase("x", ""));
            assertTrue(Ascii.equalsIgnoreCase(LOWER, UPPER));
            assertTrue(Ascii.equalsIgnoreCase(UPPER, LOWER));
            // Create new strings here to avoid early-out logic.
            assertTrue(Ascii.equalsIgnoreCase(new String(IGNORED), new String(IGNORED)));
            // Compare to: "\u00c1".equalsIgnoreCase("\u00e1") == true
            assertFalse(Ascii.equalsIgnoreCase("\u00c1", "\u00e1"));
            // Test chars just outside the alphabetic range ('A'-1 vs 'a'-1, 'Z'+1 vs 'z'+1)
            assertFalse(Ascii.equalsIgnoreCase("@", "`"));
            assertFalse(Ascii.equalsIgnoreCase("[", "{"));
        }
    
        @GwtIncompatible // String.toUpperCase() has browser semantics
        public void testEqualsIgnoreCaseUnicodeEquivalence() {
            // Note that it's possible in future that the JDK's idea to toUpperCase() or equalsIgnoreCase()
            // may change and break assumptions in this test [*]. This is not a bug in the implementation of
            // Ascii.equalsIgnoreCase(), but it is a signal that its documentation may need updating as
            // regards edge cases.
    
            // The Unicode point {@code 00df} is the lowercase form of sharp-S (ß), whose uppercase is "SS".
            assertEquals("PASSWORD", "pa\u00dfword".toUpperCase()); // [*]
            assertFalse("pa\u00dfword".equalsIgnoreCase("PASSWORD")); // [*]
            assertFalse(Ascii.equalsIgnoreCase("pa\u00dfword", "PASSWORD"));
        }
    }

2.CaseFormat大小写格式

在各种ASCII大小写格式之间进行转换的实用程序类。对于非ASCII输入,行为未定义。

CaseFormat是一个方便的枚举类,用于在ASCII大小写约定之间进行转换——例如,编程语言的命名约定。支持的格式包括:

枚举 说明 描述
LOWER_HYPHEN 小写连字符 连字符的变量命名约定,例如"lower-hyphen"
LOWER_UNDERSCORE 全小写下划线 C++变量命名约定,例如"lower_underscore"
LOWER_CAMEL 小写驼峰 Java变量命名约定,例如"lowerCamel"
UPPER_CAMEL 大写驼峰 Java和C++类的命名约定,例如"UpperCamel"
UPPER_UNDERSCORE 全大写下划线 Java和C++常量命名约定,例如"UPPER_UNDERSCORE"

使用它相对简单:

    CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, "CONSTANT_NAME")); // returns "constantName"

例如,在编写代码生成器时,我们发现这特别有用。

2.1to转换

方法:String to(CaseFormat format, String str)

将指定的String str从此格式转换为指定的format格式。采取“尽力而为”的方法;如果str不符合假定的格式,则此方法的行为未定义,但无论如何我们都会做出合理的努力。

2.2converterTo转换

方法:Converter<String, String> converterTo(CaseFormat targetFormat)

返回一个Converter,它将字符串从此格式转换为目标格式targetFormat

2.3使用示例

    import com.google.common.annotations.GwtIncompatible;
    import com.google.common.base.CaseFormat;
    import com.google.common.testing.NullPointerTester;
    import com.google.common.testing.SerializableTester;
    import junit.framework.TestCase;
    
    import static com.google.common.base.CaseFormat.*;
    
    public class CaseFormatTest extends TestCase {
    
        public void testIdentity() {
            for (CaseFormat from : CaseFormat.values()) {
                assertSame(from + " to " + from, "foo", from.to(from, "foo"));
                for (CaseFormat to : CaseFormat.values()) {
                    assertEquals(from + " to " + to, "", from.to(to, ""));
                    assertEquals(from + " to " + to, " ", from.to(to, " "));
                }
            }
        }
    
        @GwtIncompatible // NullPointerTester
        public void testNullArguments() {
            NullPointerTester tester = new NullPointerTester();
            tester.testAllPublicStaticMethods(CaseFormat.class);
            for (CaseFormat format : CaseFormat.values()) {
                tester.testAllPublicInstanceMethods(format);
            }
        }
    
        public void testLowerHyphenToLowerHyphen() {
            assertEquals("foo", LOWER_HYPHEN.to(LOWER_HYPHEN, "foo"));
            assertEquals("foo-bar", LOWER_HYPHEN.to(LOWER_HYPHEN, "foo-bar"));
        }
    
        public void testLowerHyphenToLowerUnderscore() {
            assertEquals("foo", LOWER_HYPHEN.to(LOWER_UNDERSCORE, "foo"));
            assertEquals("foo_bar", LOWER_HYPHEN.to(LOWER_UNDERSCORE, "foo-bar"));
        }
    
        public void testLowerHyphenToLowerCamel() {
            assertEquals("foo", LOWER_HYPHEN.to(LOWER_CAMEL, "foo"));
            assertEquals("fooBar", LOWER_HYPHEN.to(LOWER_CAMEL, "foo-bar"));
        }
    
        public void testLowerHyphenToUpperCamel() {
            assertEquals("Foo", LOWER_HYPHEN.to(UPPER_CAMEL, "foo"));
            assertEquals("FooBar", LOWER_HYPHEN.to(UPPER_CAMEL, "foo-bar"));
        }
    
        public void testLowerHyphenToUpperUnderscore() {
            assertEquals("FOO", LOWER_HYPHEN.to(UPPER_UNDERSCORE, "foo"));
            assertEquals("FOO_BAR", LOWER_HYPHEN.to(UPPER_UNDERSCORE, "foo-bar"));
        }
    
        public void testLowerUnderscoreToLowerHyphen() {
            assertEquals("foo", LOWER_UNDERSCORE.to(LOWER_HYPHEN, "foo"));
            assertEquals("foo-bar", LOWER_UNDERSCORE.to(LOWER_HYPHEN, "foo_bar"));
        }
    
        public void testLowerUnderscoreToLowerUnderscore() {
            assertEquals("foo", LOWER_UNDERSCORE.to(LOWER_UNDERSCORE, "foo"));
            assertEquals("foo_bar", LOWER_UNDERSCORE.to(LOWER_UNDERSCORE, "foo_bar"));
        }
    
        public void testLowerUnderscoreToLowerCamel() {
            assertEquals("foo", LOWER_UNDERSCORE.to(LOWER_CAMEL, "foo"));
            assertEquals("fooBar", LOWER_UNDERSCORE.to(LOWER_CAMEL, "foo_bar"));
        }
    
        public void testLowerUnderscoreToUpperCamel() {
            assertEquals("Foo", LOWER_UNDERSCORE.to(UPPER_CAMEL, "foo"));
            assertEquals("FooBar", LOWER_UNDERSCORE.to(UPPER_CAMEL, "foo_bar"));
        }
    
        public void testLowerUnderscoreToUpperUnderscore() {
            assertEquals("FOO", LOWER_UNDERSCORE.to(UPPER_UNDERSCORE, "foo"));
            assertEquals("FOO_BAR", LOWER_UNDERSCORE.to(UPPER_UNDERSCORE, "foo_bar"));
        }
    
        public void testLowerCamelToLowerHyphen() {
            assertEquals("foo", LOWER_CAMEL.to(LOWER_HYPHEN, "foo"));
            assertEquals("foo-bar", LOWER_CAMEL.to(LOWER_HYPHEN, "fooBar"));
            assertEquals("h-t-t-p", LOWER_CAMEL.to(LOWER_HYPHEN, "HTTP"));
        }
    
        public void testLowerCamelToLowerUnderscore() {
            assertEquals("foo", LOWER_CAMEL.to(LOWER_UNDERSCORE, "foo"));
            assertEquals("foo_bar", LOWER_CAMEL.to(LOWER_UNDERSCORE, "fooBar"));
            assertEquals("h_t_t_p", LOWER_CAMEL.to(LOWER_UNDERSCORE, "hTTP"));
        }
    
        public void testLowerCamelToLowerCamel() {
            assertEquals("foo", LOWER_CAMEL.to(LOWER_CAMEL, "foo"));
            assertEquals("fooBar", LOWER_CAMEL.to(LOWER_CAMEL, "fooBar"));
        }
    
        public void testLowerCamelToUpperCamel() {
            assertEquals("Foo", LOWER_CAMEL.to(UPPER_CAMEL, "foo"));
            assertEquals("FooBar", LOWER_CAMEL.to(UPPER_CAMEL, "fooBar"));
            assertEquals("HTTP", LOWER_CAMEL.to(UPPER_CAMEL, "hTTP"));
        }
    
        public void testLowerCamelToUpperUnderscore() {
            assertEquals("FOO", LOWER_CAMEL.to(UPPER_UNDERSCORE, "foo"));
            assertEquals("FOO_BAR", LOWER_CAMEL.to(UPPER_UNDERSCORE, "fooBar"));
        }
    
        public void testUpperCamelToLowerHyphen() {
            assertEquals("foo", UPPER_CAMEL.to(LOWER_HYPHEN, "Foo"));
            assertEquals("foo-bar", UPPER_CAMEL.to(LOWER_HYPHEN, "FooBar"));
        }
    
        public void testUpperCamelToLowerUnderscore() {
            assertEquals("foo", UPPER_CAMEL.to(LOWER_UNDERSCORE, "Foo"));
            assertEquals("foo_bar", UPPER_CAMEL.to(LOWER_UNDERSCORE, "FooBar"));
        }
    
        public void testUpperCamelToLowerCamel() {
            assertEquals("foo", UPPER_CAMEL.to(LOWER_CAMEL, "Foo"));
            assertEquals("fooBar", UPPER_CAMEL.to(LOWER_CAMEL, "FooBar"));
            assertEquals("hTTP", UPPER_CAMEL.to(LOWER_CAMEL, "HTTP"));
        }
    
        public void testUpperCamelToUpperCamel() {
            assertEquals("Foo", UPPER_CAMEL.to(UPPER_CAMEL, "Foo"));
            assertEquals("FooBar", UPPER_CAMEL.to(UPPER_CAMEL, "FooBar"));
        }
    
        public void testUpperCamelToUpperUnderscore() {
            assertEquals("FOO", UPPER_CAMEL.to(UPPER_UNDERSCORE, "Foo"));
            assertEquals("FOO_BAR", UPPER_CAMEL.to(UPPER_UNDERSCORE, "FooBar"));
            assertEquals("H_T_T_P", UPPER_CAMEL.to(UPPER_UNDERSCORE, "HTTP"));
            assertEquals("H__T__T__P", UPPER_CAMEL.to(UPPER_UNDERSCORE, "H_T_T_P"));
        }
    
        public void testUpperUnderscoreToLowerHyphen() {
            assertEquals("foo", UPPER_UNDERSCORE.to(LOWER_HYPHEN, "FOO"));
            assertEquals("foo-bar", UPPER_UNDERSCORE.to(LOWER_HYPHEN, "FOO_BAR"));
        }
    
        public void testUpperUnderscoreToLowerUnderscore() {
            assertEquals("foo", UPPER_UNDERSCORE.to(LOWER_UNDERSCORE, "FOO"));
            assertEquals("foo_bar", UPPER_UNDERSCORE.to(LOWER_UNDERSCORE, "FOO_BAR"));
        }
    
        public void testUpperUnderscoreToLowerCamel() {
            assertEquals("foo", UPPER_UNDERSCORE.to(LOWER_CAMEL, "FOO"));
            assertEquals("fooBar", UPPER_UNDERSCORE.to(LOWER_CAMEL, "FOO_BAR"));
        }
    
        public void testUpperUnderscoreToUpperCamel() {
            assertEquals("Foo", UPPER_UNDERSCORE.to(UPPER_CAMEL, "FOO"));
            assertEquals("FooBar", UPPER_UNDERSCORE.to(UPPER_CAMEL, "FOO_BAR"));
            assertEquals("HTTP", UPPER_UNDERSCORE.to(UPPER_CAMEL, "H_T_T_P"));
        }
    
        public void testUpperUnderscoreToUpperUnderscore() {
            assertEquals("FOO", UPPER_UNDERSCORE.to(UPPER_UNDERSCORE, "FOO"));
            assertEquals("FOO_BAR", UPPER_UNDERSCORE.to(UPPER_UNDERSCORE, "FOO_BAR"));
        }
    
        public void testConverterToForward() {
            assertEquals("FooBar", UPPER_UNDERSCORE.converterTo(UPPER_CAMEL).convert("FOO_BAR"));
            assertEquals("fooBar", UPPER_UNDERSCORE.converterTo(LOWER_CAMEL).convert("FOO_BAR"));
            assertEquals("FOO_BAR", UPPER_CAMEL.converterTo(UPPER_UNDERSCORE).convert("FooBar"));
            assertEquals("FOO_BAR", LOWER_CAMEL.converterTo(UPPER_UNDERSCORE).convert("fooBar"));
        }
    
        public void testConverterToBackward() {
            assertEquals("FOO_BAR", UPPER_UNDERSCORE.converterTo(UPPER_CAMEL).reverse().convert("FooBar"));
            assertEquals("FOO_BAR", UPPER_UNDERSCORE.converterTo(LOWER_CAMEL).reverse().convert("fooBar"));
            assertEquals("FooBar", UPPER_CAMEL.converterTo(UPPER_UNDERSCORE).reverse().convert("FOO_BAR"));
            assertEquals("fooBar", LOWER_CAMEL.converterTo(UPPER_UNDERSCORE).reverse().convert("FOO_BAR"));
        }
    
        public void testConverter_nullConversions() {
            for (CaseFormat outer : CaseFormat.values()) {
                for (CaseFormat inner : CaseFormat.values()) {
                    assertNull(outer.converterTo(inner).convert(null));
                    assertNull(outer.converterTo(inner).reverse().convert(null));
                }
            }
        }
    
        public void testConverter_toString() {
            assertEquals(
    	        "LOWER_HYPHEN.converterTo(UPPER_CAMEL)", LOWER_HYPHEN.converterTo(UPPER_CAMEL).toString());
        }
    
        public void testConverter_serialization() {
            for (CaseFormat outer : CaseFormat.values()) {
                for (CaseFormat inner : CaseFormat.values()) {
                    SerializableTester.reserializeAndAssert(outer.converterTo(inner));
                }
            }
        }
        
    }

3.CharMatcher字符匹配器

确定任何Java中char值是真还是假,就像Predicate对任何Object所做的一样。还提供了基于此函数的基本文本处理方法。强烈建议实现的过程必须无副作用且不可变。

在此类的整个文档中,短语“匹配字符”用于表示"任何charc,其中this.matches(c)返回true"。

警告: 此类仅处理char值,即BMP字符。它不理解0x100000x10FFFF范围内的补充Unicode代码点,该范围包括大多数分配的字符,包括重要的CJK字符和表情符号。

补充字符使用代理对编码为String,而CharMatcher将这些字符视为两个单独的字符。 countIn将每个补充字符计为2个chars。

对于最新的Unicode字符属性(数字,字母等)并支持补充代码点,请使用ICU4J UCharacterUnicodeSet(构建后为freeze())。对于基于UnicodeSet的基本文本处理,请使用ICU4J UnicodeSetSpanner

使用示例:

    String trimmed = whitespace().trimFrom(userInput);
    if (ascii().matchesAllOf(s)) { ... }
    

在过去,我们的StringUtil类不受控制地增长,并且具有许多类似这样的方法:
allAsciicollapsecollapseControlCharscollapseWhitespacelastIndexNotOfnumSharedCharsremoveCharsremoveCrLfretainAllCharsstripstripAndCollapsestripNonDigits

它们代表两个概念的部分交叉:

  1. 什么构成“匹配”字符?
  2. 如何处理这些“匹配”的字符?

为了简化这一难题,开发了CharMatcher

直观地,可以将CharMatcher视为代表特定类别的字符,例如数字或空格。实际上,CharMatcher只是有关字符的布尔谓词——事实上,CharMatcher实现了[Predicate<Character>]——但是由于引用“所有空白字符”或“所有小写字母”太普遍了,因此Guava提供了专门的字符语法和API。

但是CharMatcher的实用工具包含在操作中,它使你可以在出现指定类别的字符时执行:修剪[trimming],折叠[collapsing],移除[removing],保留[retaining]等等。CharMatcher类型的对象表示概念1:什么构成匹配字符?然后,它提供了许多回答概念2的操作:如何处理这些匹配的字符?结果是,API复杂度呈线性增加,而灵活性和功能则成倍增加。好极了!

    String noControl = CharMatcher.javaIsoControl().removeFrom(string); // remove control characters
    String theDigits = CharMatcher.digit().retainFrom(string); // only the digits
    String spaced = CharMatcher.whitespace().trimAndCollapseFrom(string, ' ');
      // trim whitespace at ends, and replace/collapse whitespace into single spaces
    String noDigits = CharMatcher.javaDigit().replaceFrom(string, "*"); // star out all digits
    String lowerAndDigit = CharMatcher.javaDigit().or(CharMatcher.javaLowerCase()).retainFrom(string);
      // eliminate all characters that aren't digits or lowercase

注意: CharMatcher仅处理char值;它不能理解0x100000x10FFFF范围内的补充Unicode代码点。使用代理对将此类逻辑字符编码为String,并且CharMatcher将这些字符视为两个单独的字符。

3.1获取字符匹配器

提供的CharMatcher工厂方法可以满足许多需求:

获取CharMatcher的其它常见方法包括:

方法 描述
anyOf(CharSequence) 指定希望匹配的所有字符。例如,CharMatcher.anyOf("aeiou")匹配小写英语元音。
is(char) 仅指定一个字符进行匹配。
inRange(char,char) 指定要匹配的字符范围,示例:CharMatcher.inRange('a','z')。

此外,CharMatcher具有negate()and(CharMatcher)or(CharMatcher)。这些在CharMatcher上提供了简单的布尔操作。

3.2使用字符匹配器

CharMatcher提供了多种多样方法来处理任何CharSequence中出现的指定字符。提供的方法比我们在此处列出的方法更多,但是一些最常用的方法是:

方法 描述
collapseFrom(CharSequence,char) 将每组连续匹配的字符替换为指定的字符。例如,WHITESPACE.collapseFrom(string,'')将空格折叠为单个空格。
matchesAllOf(CharSequence) 测试此匹配器是否匹配序列中的所有字符。例如,ASCII.matchesAllOf(string)测试字符串中的所有字符是否均为ASCII。
removeFrom(CharSequence) 从序列中删除匹配的字符。
retainFrom(CharSequence) 从序列中删除所有不匹配的字符。
trimFrom(CharSequence) 删除开头和结尾的匹配字符。
replaceFrom(CharSequence,CharSequence) 用给定的序列替换匹配的字符。

(注意:所有这些方法都返回一个String,但matchesAllOf除外,后者返回一个boolean布尔值。)

3.3使用示例

    import com.google.common.annotations.GwtIncompatible;
    import com.google.common.base.CharMatcher;
    import com.google.common.base.Predicates;
    import com.google.common.base.Strings;
    import com.google.common.testing.NullPointerTester;
    import junit.framework.AssertionFailedError;
    import junit.framework.TestCase;
    import static com.google.common.base.CharMatcher.*;
    
    public class CharMatcherTest extends TestCase {
    
        @GwtIncompatible // NullPointerTester
        public void testStaticNullPointers() throws Exception {
            NullPointerTester tester = new NullPointerTester();
            tester.testAllPublicStaticMethods(CharMatcher.class);
            tester.testAllPublicInstanceMethods(CharMatcher.any());
            tester.testAllPublicInstanceMethods(anyOf("abc"));
        }
    
        private static final CharMatcher WHATEVER =
                new CharMatcher() {
                    @Override
                    public boolean matches(char c) {
                        throw new AssertionFailedError("You weren't supposed to actually invoke me!");
                    }
                };
    
        public void testAnyAndNone_logicalOps() throws Exception {
            // These are testing behavior that's never promised by the API, but since
            // we're lucky enough that these do pass, it saves us from having to write
            // more excruciating tests! Hooray!
    
            assertSame(CharMatcher.any(), CharMatcher.none().negate());
            assertSame(CharMatcher.none(), CharMatcher.any().negate());
    
            assertSame(WHATEVER, CharMatcher.any().and(WHATEVER));
            assertSame(CharMatcher.any(), CharMatcher.any().or(WHATEVER));
    
            assertSame(CharMatcher.none(), CharMatcher.none().and(WHATEVER));
            assertSame(WHATEVER, CharMatcher.none().or(WHATEVER));
        }
    
        // The rest of the behavior of ANY and DEFAULT will be covered in the tests for
        // the text processing methods below.
    
        public void testWhitespaceBreakingWhitespaceSubset() throws Exception {
            for (int c = 0; c <= Character.MAX_VALUE; c++) {
                if (breakingWhitespace().matches((char) c)) {
                    assertTrue(Integer.toHexString(c), whitespace().matches((char) c));
                }
            }
        }
    
        // The next tests require ICU4J and have, at least for now, been sliced out
        // of the open-source view of the tests.
    
        @GwtIncompatible // Character.isISOControl
        public void testJavaIsoControl() {
            for (int c = 0; c <= Character.MAX_VALUE; c++) {
                assertEquals(
                        "" + c, Character.isISOControl(c), CharMatcher.javaIsoControl().matches((char) c));
            }
        }
    
        // Omitting tests for the rest of the JAVA_* constants as these are defined
        // as extremely straightforward pass-throughs to the JDK methods.
    
        // We're testing the is(), isNot(), anyOf(), noneOf() and inRange() methods
        // below by testing their text-processing methods.
    
        // The organization of this test class is unusual, as it's not done by
        // method, but by overall "scenario". Also, the variety of actual tests we
        // do borders on absurd overkill. Better safe than sorry, though?
    
        public void testEmpty() throws Exception {
            doTestEmpty(CharMatcher.any());
            doTestEmpty(CharMatcher.none());
            doTestEmpty(is('a'));
            doTestEmpty(isNot('a'));
            doTestEmpty(anyOf(""));
            doTestEmpty(anyOf("x"));
            doTestEmpty(anyOf("xy"));
            doTestEmpty(anyOf("CharMatcher"));
            doTestEmpty(noneOf("CharMatcher"));
            doTestEmpty(inRange('n', 'q'));
            doTestEmpty(forPredicate(Predicates.equalTo('c')));
        }
    
        @GwtIncompatible // NullPointerTester
        public void testNull() throws Exception {
            doTestNull(CharMatcher.any());
            doTestNull(CharMatcher.none());
            doTestNull(is('a'));
            doTestNull(isNot('a'));
            doTestNull(anyOf(""));
            doTestNull(anyOf("x"));
            doTestNull(anyOf("xy"));
            doTestNull(anyOf("CharMatcher"));
            doTestNull(noneOf("CharMatcher"));
            doTestNull(inRange('n', 'q'));
            doTestNull(forPredicate(Predicates.equalTo('c')));
        }
    
        private void doTestEmpty(CharMatcher matcher) throws Exception {
            reallyTestEmpty(matcher);
            reallyTestEmpty(matcher.negate());
            reallyTestEmpty(matcher.precomputed());
        }
    
        private void reallyTestEmpty(CharMatcher matcher) throws Exception {
            assertEquals(-1, matcher.indexIn(""));
            assertEquals(-1, matcher.indexIn("", 0));
            try {
                matcher.indexIn("", 1);
                fail();
            } catch (IndexOutOfBoundsException expected) {
            }
            try {
                matcher.indexIn("", -1);
                fail();
            } catch (IndexOutOfBoundsException expected) {
            }
            assertEquals(-1, matcher.lastIndexIn(""));
            assertFalse(matcher.matchesAnyOf(""));
            assertTrue(matcher.matchesAllOf(""));
            assertTrue(matcher.matchesNoneOf(""));
            assertEquals("", matcher.removeFrom(""));
            assertEquals("", matcher.replaceFrom("", 'z'));
            assertEquals("", matcher.replaceFrom("", "ZZ"));
            assertEquals("", matcher.trimFrom(""));
            assertEquals(0, matcher.countIn(""));
        }
    
        @GwtIncompatible // NullPointerTester
        private static void doTestNull(CharMatcher matcher) throws Exception {
            NullPointerTester tester = new NullPointerTester();
            tester.testAllPublicInstanceMethods(matcher);
        }
    
        public void testNoMatches() {
            doTestNoMatches(CharMatcher.none(), "blah");
            doTestNoMatches(is('a'), "bcde");
            doTestNoMatches(isNot('a'), "aaaa");
            doTestNoMatches(anyOf(""), "abcd");
            doTestNoMatches(anyOf("x"), "abcd");
            doTestNoMatches(anyOf("xy"), "abcd");
            doTestNoMatches(anyOf("CharMatcher"), "zxqy");
            doTestNoMatches(noneOf("CharMatcher"), "ChMa");
            doTestNoMatches(inRange('p', 'x'), "mom");
            doTestNoMatches(forPredicate(Predicates.equalTo('c')), "abe");
            doTestNoMatches(inRange('A', 'Z').and(inRange('F', 'K').negate()), "F1a");
            doTestNoMatches(CharMatcher.digit(), "\tAz()");
            doTestNoMatches(CharMatcher.javaDigit(), "\tAz()");
            doTestNoMatches(CharMatcher.digit().and(CharMatcher.ascii()), "\tAz()");
            doTestNoMatches(CharMatcher.singleWidth(), "\u05bf\u3000");
        }
    
        private void doTestNoMatches(CharMatcher matcher, String s) {
            reallyTestNoMatches(matcher, s);
            reallyTestAllMatches(matcher.negate(), s);
            reallyTestNoMatches(matcher.precomputed(), s);
            reallyTestAllMatches(matcher.negate().precomputed(), s);
            reallyTestAllMatches(matcher.precomputed().negate(), s);
            reallyTestNoMatches(forPredicate(matcher), s);
    
            reallyTestNoMatches(matcher, new StringBuilder(s));
        }
    
        public void testAllMatches() {
            doTestAllMatches(CharMatcher.any(), "blah");
            doTestAllMatches(isNot('a'), "bcde");
            doTestAllMatches(is('a'), "aaaa");
            doTestAllMatches(noneOf("CharMatcher"), "zxqy");
            doTestAllMatches(anyOf("x"), "xxxx");
            doTestAllMatches(anyOf("xy"), "xyyx");
            doTestAllMatches(anyOf("CharMatcher"), "ChMa");
            doTestAllMatches(inRange('m', 'p'), "mom");
            doTestAllMatches(forPredicate(Predicates.equalTo('c')), "ccc");
            doTestAllMatches(CharMatcher.digit(), "0123456789\u0ED0\u1B59");
            doTestAllMatches(CharMatcher.javaDigit(), "0123456789");
            doTestAllMatches(CharMatcher.digit().and(CharMatcher.ascii()), "0123456789");
            doTestAllMatches(CharMatcher.singleWidth(), "\t0123ABCdef~\u00A0\u2111");
        }
    
        private void doTestAllMatches(CharMatcher matcher, String s) {
            reallyTestAllMatches(matcher, s);
            reallyTestNoMatches(matcher.negate(), s);
            reallyTestAllMatches(matcher.precomputed(), s);
            reallyTestNoMatches(matcher.negate().precomputed(), s);
            reallyTestNoMatches(matcher.precomputed().negate(), s);
            reallyTestAllMatches(forPredicate(matcher), s);
    
            reallyTestAllMatches(matcher, new StringBuilder(s));
        }
    
        private void reallyTestNoMatches(CharMatcher matcher, CharSequence s) {
            assertFalse(matcher.matches(s.charAt(0)));
            assertEquals(-1, matcher.indexIn(s));
            assertEquals(-1, matcher.indexIn(s, 0));
            assertEquals(-1, matcher.indexIn(s, 1));
            assertEquals(-1, matcher.indexIn(s, s.length()));
            try {
                matcher.indexIn(s, s.length() + 1);
                fail();
            } catch (IndexOutOfBoundsException expected) {
            }
            try {
                matcher.indexIn(s, -1);
                fail();
            } catch (IndexOutOfBoundsException expected) {
            }
            assertEquals(-1, matcher.lastIndexIn(s));
            assertFalse(matcher.matchesAnyOf(s));
            assertFalse(matcher.matchesAllOf(s));
            assertTrue(matcher.matchesNoneOf(s));
    
            assertEquals(s.toString(), matcher.removeFrom(s));
            assertEquals(s.toString(), matcher.replaceFrom(s, 'z'));
            assertEquals(s.toString(), matcher.replaceFrom(s, "ZZ"));
            assertEquals(s.toString(), matcher.trimFrom(s));
            assertEquals(0, matcher.countIn(s));
        }
    
        private void reallyTestAllMatches(CharMatcher matcher, CharSequence s) {
            assertTrue(matcher.matches(s.charAt(0)));
            assertEquals(0, matcher.indexIn(s));
            assertEquals(0, matcher.indexIn(s, 0));
            assertEquals(1, matcher.indexIn(s, 1));
            assertEquals(-1, matcher.indexIn(s, s.length()));
            assertEquals(s.length() - 1, matcher.lastIndexIn(s));
            assertTrue(matcher.matchesAnyOf(s));
            assertTrue(matcher.matchesAllOf(s));
            assertFalse(matcher.matchesNoneOf(s));
            assertEquals("", matcher.removeFrom(s));
            assertEquals(Strings.repeat("z", s.length()), matcher.replaceFrom(s, 'z'));
            assertEquals(Strings.repeat("ZZ", s.length()), matcher.replaceFrom(s, "ZZ"));
            assertEquals("", matcher.trimFrom(s));
            assertEquals(s.length(), matcher.countIn(s));
        }
    
        public void testGeneral() {
            doTestGeneral(is('a'), 'a', 'b');
            doTestGeneral(isNot('a'), 'b', 'a');
            doTestGeneral(anyOf("x"), 'x', 'z');
            doTestGeneral(anyOf("xy"), 'y', 'z');
            doTestGeneral(anyOf("CharMatcher"), 'C', 'z');
            doTestGeneral(noneOf("CharMatcher"), 'z', 'C');
            doTestGeneral(inRange('p', 'x'), 'q', 'z');
        }
    
        private void doTestGeneral(CharMatcher matcher, char match, char noMatch) {
            doTestOneCharMatch(matcher, "" + match);
            doTestOneCharNoMatch(matcher, "" + noMatch);
            doTestMatchThenNoMatch(matcher, "" + match + noMatch);
            doTestNoMatchThenMatch(matcher, "" + noMatch + match);
        }
    
        private void doTestOneCharMatch(CharMatcher matcher, String s) {
            reallyTestOneCharMatch(matcher, s);
            reallyTestOneCharNoMatch(matcher.negate(), s);
            reallyTestOneCharMatch(matcher.precomputed(), s);
            reallyTestOneCharNoMatch(matcher.negate().precomputed(), s);
            reallyTestOneCharNoMatch(matcher.precomputed().negate(), s);
        }
    
        private void doTestOneCharNoMatch(CharMatcher matcher, String s) {
            reallyTestOneCharNoMatch(matcher, s);
            reallyTestOneCharMatch(matcher.negate(), s);
            reallyTestOneCharNoMatch(matcher.precomputed(), s);
            reallyTestOneCharMatch(matcher.negate().precomputed(), s);
            reallyTestOneCharMatch(matcher.precomputed().negate(), s);
        }
    
        private void doTestMatchThenNoMatch(CharMatcher matcher, String s) {
            reallyTestMatchThenNoMatch(matcher, s);
            reallyTestNoMatchThenMatch(matcher.negate(), s);
            reallyTestMatchThenNoMatch(matcher.precomputed(), s);
            reallyTestNoMatchThenMatch(matcher.negate().precomputed(), s);
            reallyTestNoMatchThenMatch(matcher.precomputed().negate(), s);
        }
    
        private void doTestNoMatchThenMatch(CharMatcher matcher, String s) {
            reallyTestNoMatchThenMatch(matcher, s);
            reallyTestMatchThenNoMatch(matcher.negate(), s);
            reallyTestNoMatchThenMatch(matcher.precomputed(), s);
            reallyTestMatchThenNoMatch(matcher.negate().precomputed(), s);
            reallyTestMatchThenNoMatch(matcher.precomputed().negate(), s);
        }
    
        @SuppressWarnings("deprecation") // intentionally testing apply() method
        private void reallyTestOneCharMatch(CharMatcher matcher, String s) {
            assertTrue(matcher.matches(s.charAt(0)));
            assertTrue(matcher.apply(s.charAt(0)));
            assertEquals(0, matcher.indexIn(s));
            assertEquals(0, matcher.indexIn(s, 0));
            assertEquals(-1, matcher.indexIn(s, 1));
            assertEquals(0, matcher.lastIndexIn(s));
            assertTrue(matcher.matchesAnyOf(s));
            assertTrue(matcher.matchesAllOf(s));
            assertFalse(matcher.matchesNoneOf(s));
            assertEquals("", matcher.removeFrom(s));
            assertEquals("z", matcher.replaceFrom(s, 'z'));
            assertEquals("ZZ", matcher.replaceFrom(s, "ZZ"));
            assertEquals("", matcher.trimFrom(s));
            assertEquals(1, matcher.countIn(s));
        }
    
        @SuppressWarnings("deprecation") // intentionally testing apply() method
        private void reallyTestOneCharNoMatch(CharMatcher matcher, String s) {
            assertFalse(matcher.matches(s.charAt(0)));
            assertFalse(matcher.apply(s.charAt(0)));
            assertEquals(-1, matcher.indexIn(s));
            assertEquals(-1, matcher.indexIn(s, 0));
            assertEquals(-1, matcher.indexIn(s, 1));
            assertEquals(-1, matcher.lastIndexIn(s));
            assertFalse(matcher.matchesAnyOf(s));
            assertFalse(matcher.matchesAllOf(s));
            assertTrue(matcher.matchesNoneOf(s));
    
            assertSame(s, matcher.removeFrom(s));
            assertSame(s, matcher.replaceFrom(s, 'z'));
            assertSame(s, matcher.replaceFrom(s, "ZZ"));
            assertSame(s, matcher.trimFrom(s));
            assertSame(0, matcher.countIn(s));
        }
    
        private void reallyTestMatchThenNoMatch(CharMatcher matcher, String s) {
            assertEquals(0, matcher.indexIn(s));
            assertEquals(0, matcher.indexIn(s, 0));
            assertEquals(-1, matcher.indexIn(s, 1));
            assertEquals(-1, matcher.indexIn(s, 2));
            assertEquals(0, matcher.lastIndexIn(s));
            assertTrue(matcher.matchesAnyOf(s));
            assertFalse(matcher.matchesAllOf(s));
            assertFalse(matcher.matchesNoneOf(s));
            assertEquals(s.substring(1), matcher.removeFrom(s));
            assertEquals("z" + s.substring(1), matcher.replaceFrom(s, 'z'));
            assertEquals("ZZ" + s.substring(1), matcher.replaceFrom(s, "ZZ"));
            assertEquals(s.substring(1), matcher.trimFrom(s));
            assertEquals(1, matcher.countIn(s));
        }
    
        private void reallyTestNoMatchThenMatch(CharMatcher matcher, String s) {
            assertEquals(1, matcher.indexIn(s));
            assertEquals(1, matcher.indexIn(s, 0));
            assertEquals(1, matcher.indexIn(s, 1));
            assertEquals(-1, matcher.indexIn(s, 2));
            assertEquals(1, matcher.lastIndexIn(s));
            assertTrue(matcher.matchesAnyOf(s));
            assertFalse(matcher.matchesAllOf(s));
            assertFalse(matcher.matchesNoneOf(s));
            assertEquals(s.substring(0, 1), matcher.removeFrom(s));
            assertEquals(s.substring(0, 1) + "z", matcher.replaceFrom(s, 'z'));
            assertEquals(s.substring(0, 1) + "ZZ", matcher.replaceFrom(s, "ZZ"));
            assertEquals(s.substring(0, 1), matcher.trimFrom(s));
            assertEquals(1, matcher.countIn(s));
        }
    
        /**
         * Checks that expected is equals to out, and further, if in is equals to expected, then out is
         * successfully optimized to be identical to in, i.e. that "in" is simply returned.
         */
        private void assertEqualsSame(String expected, String in, String out) {
            if (expected.equals(in)) {
                assertSame(in, out);
            } else {
                assertEquals(expected, out);
            }
        }
    
        // Test collapse() a little differently than the rest, as we really want to
        // cover lots of different configurations of input text
        public void testCollapse() {
            // collapsing groups of '-' into '_' or '-'
            doTestCollapse("-", "_");
            doTestCollapse("x-", "x_");
            doTestCollapse("-x", "_x");
            doTestCollapse("--", "_");
            doTestCollapse("x--", "x_");
            doTestCollapse("--x", "_x");
            doTestCollapse("-x-", "_x_");
            doTestCollapse("x-x", "x_x");
            doTestCollapse("---", "_");
            doTestCollapse("--x-", "_x_");
            doTestCollapse("--xx", "_xx");
            doTestCollapse("-x--", "_x_");
            doTestCollapse("-x-x", "_x_x");
            doTestCollapse("-xx-", "_xx_");
            doTestCollapse("x--x", "x_x");
            doTestCollapse("x-x-", "x_x_");
            doTestCollapse("x-xx", "x_xx");
            doTestCollapse("x-x--xx---x----x", "x_x_xx_x_x");
    
            doTestCollapseWithNoChange("");
            doTestCollapseWithNoChange("x");
            doTestCollapseWithNoChange("xx");
        }
    
        private void doTestCollapse(String in, String out) {
            // Try a few different matchers which all match '-' and not 'x'
            // Try replacement chars that both do and do not change the value.
            for (char replacement : new char[]{'_', '-'}) {
                String expected = out.replace('_', replacement);
                assertEqualsSame(expected, in, is('-').collapseFrom(in, replacement));
                assertEqualsSame(expected, in, is('-').collapseFrom(in, replacement));
                assertEqualsSame(expected, in, is('-').or(is('#')).collapseFrom(in, replacement));
                assertEqualsSame(expected, in, isNot('x').collapseFrom(in, replacement));
                assertEqualsSame(expected, in, is('x').negate().collapseFrom(in, replacement));
                assertEqualsSame(expected, in, anyOf("-").collapseFrom(in, replacement));
                assertEqualsSame(expected, in, anyOf("-#").collapseFrom(in, replacement));
                assertEqualsSame(expected, in, anyOf("-#123").collapseFrom(in, replacement));
            }
        }
    
        private void doTestCollapseWithNoChange(String inout) {
            assertSame(inout, is('-').collapseFrom(inout, '_'));
            assertSame(inout, is('-').or(is('#')).collapseFrom(inout, '_'));
            assertSame(inout, isNot('x').collapseFrom(inout, '_'));
            assertSame(inout, is('x').negate().collapseFrom(inout, '_'));
            assertSame(inout, anyOf("-").collapseFrom(inout, '_'));
            assertSame(inout, anyOf("-#").collapseFrom(inout, '_'));
            assertSame(inout, anyOf("-#123").collapseFrom(inout, '_'));
            assertSame(inout, CharMatcher.none().collapseFrom(inout, '_'));
        }
    
        public void testCollapse_any() {
            assertEquals("", CharMatcher.any().collapseFrom("", '_'));
            assertEquals("_", CharMatcher.any().collapseFrom("a", '_'));
            assertEquals("_", CharMatcher.any().collapseFrom("ab", '_'));
            assertEquals("_", CharMatcher.any().collapseFrom("abcd", '_'));
        }
    
        public void testTrimFrom() {
            // trimming -
            doTestTrimFrom("-", "");
            doTestTrimFrom("x-", "x");
            doTestTrimFrom("-x", "x");
            doTestTrimFrom("--", "");
            doTestTrimFrom("x--", "x");
            doTestTrimFrom("--x", "x");
            doTestTrimFrom("-x-", "x");
            doTestTrimFrom("x-x", "x-x");
            doTestTrimFrom("---", "");
            doTestTrimFrom("--x-", "x");
            doTestTrimFrom("--xx", "xx");
            doTestTrimFrom("-x--", "x");
            doTestTrimFrom("-x-x", "x-x");
            doTestTrimFrom("-xx-", "xx");
            doTestTrimFrom("x--x", "x--x");
            doTestTrimFrom("x-x-", "x-x");
            doTestTrimFrom("x-xx", "x-xx");
            doTestTrimFrom("x-x--xx---x----x", "x-x--xx---x----x");
            // additional testing using the doc example
            assertEquals("cat", anyOf("ab").trimFrom("abacatbab"));
        }
    
        private void doTestTrimFrom(String in, String out) {
            // Try a few different matchers which all match '-' and not 'x'
            assertEquals(out, is('-').trimFrom(in));
            assertEquals(out, is('-').or(is('#')).trimFrom(in));
            assertEquals(out, isNot('x').trimFrom(in));
            assertEquals(out, is('x').negate().trimFrom(in));
            assertEquals(out, anyOf("-").trimFrom(in));
            assertEquals(out, anyOf("-#").trimFrom(in));
            assertEquals(out, anyOf("-#123").trimFrom(in));
        }
    
        public void testTrimLeadingFrom() {
            // trimming -
            doTestTrimLeadingFrom("-", "");
            doTestTrimLeadingFrom("x-", "x-");
            doTestTrimLeadingFrom("-x", "x");
            doTestTrimLeadingFrom("--", "");
            doTestTrimLeadingFrom("x--", "x--");
            doTestTrimLeadingFrom("--x", "x");
            doTestTrimLeadingFrom("-x-", "x-");
            doTestTrimLeadingFrom("x-x", "x-x");
            doTestTrimLeadingFrom("---", "");
            doTestTrimLeadingFrom("--x-", "x-");
            doTestTrimLeadingFrom("--xx", "xx");
            doTestTrimLeadingFrom("-x--", "x--");
            doTestTrimLeadingFrom("-x-x", "x-x");
            doTestTrimLeadingFrom("-xx-", "xx-");
            doTestTrimLeadingFrom("x--x", "x--x");
            doTestTrimLeadingFrom("x-x-", "x-x-");
            doTestTrimLeadingFrom("x-xx", "x-xx");
            doTestTrimLeadingFrom("x-x--xx---x----x", "x-x--xx---x----x");
            // additional testing using the doc example
            assertEquals("catbab", anyOf("ab").trimLeadingFrom("abacatbab"));
        }
    
        private void doTestTrimLeadingFrom(String in, String out) {
            // Try a few different matchers which all match '-' and not 'x'
            assertEquals(out, is('-').trimLeadingFrom(in));
            assertEquals(out, is('-').or(is('#')).trimLeadingFrom(in));
            assertEquals(out, isNot('x').trimLeadingFrom(in));
            assertEquals(out, is('x').negate().trimLeadingFrom(in));
            assertEquals(out, anyOf("-#").trimLeadingFrom(in));
            assertEquals(out, anyOf("-#123").trimLeadingFrom(in));
        }
    
        public void testTrimTrailingFrom() {
            // trimming -
            doTestTrimTrailingFrom("-", "");
            doTestTrimTrailingFrom("x-", "x");
            doTestTrimTrailingFrom("-x", "-x");
            doTestTrimTrailingFrom("--", "");
            doTestTrimTrailingFrom("x--", "x");
            doTestTrimTrailingFrom("--x", "--x");
            doTestTrimTrailingFrom("-x-", "-x");
            doTestTrimTrailingFrom("x-x", "x-x");
            doTestTrimTrailingFrom("---", "");
            doTestTrimTrailingFrom("--x-", "--x");
            doTestTrimTrailingFrom("--xx", "--xx");
            doTestTrimTrailingFrom("-x--", "-x");
            doTestTrimTrailingFrom("-x-x", "-x-x");
            doTestTrimTrailingFrom("-xx-", "-xx");
            doTestTrimTrailingFrom("x--x", "x--x");
            doTestTrimTrailingFrom("x-x-", "x-x");
            doTestTrimTrailingFrom("x-xx", "x-xx");
            doTestTrimTrailingFrom("x-x--xx---x----x", "x-x--xx---x----x");
            // additional testing using the doc example
            assertEquals("abacat", anyOf("ab").trimTrailingFrom("abacatbab"));
        }
    
        private void doTestTrimTrailingFrom(String in, String out) {
            // Try a few different matchers which all match '-' and not 'x'
            assertEquals(out, is('-').trimTrailingFrom(in));
            assertEquals(out, is('-').or(is('#')).trimTrailingFrom(in));
            assertEquals(out, isNot('x').trimTrailingFrom(in));
            assertEquals(out, is('x').negate().trimTrailingFrom(in));
            assertEquals(out, anyOf("-#").trimTrailingFrom(in));
            assertEquals(out, anyOf("-#123").trimTrailingFrom(in));
        }
    
        public void testTrimAndCollapse() {
            // collapsing groups of '-' into '_' or '-'
            doTestTrimAndCollapse("", "");
            doTestTrimAndCollapse("x", "x");
            doTestTrimAndCollapse("-", "");
            doTestTrimAndCollapse("x-", "x");
            doTestTrimAndCollapse("-x", "x");
            doTestTrimAndCollapse("--", "");
            doTestTrimAndCollapse("x--", "x");
            doTestTrimAndCollapse("--x", "x");
            doTestTrimAndCollapse("-x-", "x");
            doTestTrimAndCollapse("x-x", "x_x");
            doTestTrimAndCollapse("---", "");
            doTestTrimAndCollapse("--x-", "x");
            doTestTrimAndCollapse("--xx", "xx");
            doTestTrimAndCollapse("-x--", "x");
            doTestTrimAndCollapse("-x-x", "x_x");
            doTestTrimAndCollapse("-xx-", "xx");
            doTestTrimAndCollapse("x--x", "x_x");
            doTestTrimAndCollapse("x-x-", "x_x");
            doTestTrimAndCollapse("x-xx", "x_xx");
            doTestTrimAndCollapse("x-x--xx---x----x", "x_x_xx_x_x");
        }
    
        private void doTestTrimAndCollapse(String in, String out) {
            // Try a few different matchers which all match '-' and not 'x'
            for (char replacement : new char[]{'_', '-'}) {
                String expected = out.replace('_', replacement);
                assertEqualsSame(expected, in, is('-').trimAndCollapseFrom(in, replacement));
                assertEqualsSame(expected, in, is('-').or(is('#')).trimAndCollapseFrom(in, replacement));
                assertEqualsSame(expected, in, isNot('x').trimAndCollapseFrom(in, replacement));
                assertEqualsSame(expected, in, is('x').negate().trimAndCollapseFrom(in, replacement));
                assertEqualsSame(expected, in, anyOf("-").trimAndCollapseFrom(in, replacement));
                assertEqualsSame(expected, in, anyOf("-#").trimAndCollapseFrom(in, replacement));
                assertEqualsSame(expected, in, anyOf("-#123").trimAndCollapseFrom(in, replacement));
            }
        }
    
        public void testReplaceFrom() {
            assertEquals("yoho", is('a').replaceFrom("yaha", 'o'));
            assertEquals("yh", is('a').replaceFrom("yaha", ""));
            assertEquals("yoho", is('a').replaceFrom("yaha", "o"));
            assertEquals("yoohoo", is('a').replaceFrom("yaha", "oo"));
            assertEquals("12 > 5", is('>').replaceFrom("12 > 5", ">"));
        }
    
        public void testPrecomputedOptimizations() {
            // These are testing behavior that's never promised by the API.
            // Some matchers are so efficient that it is a waste of effort to
            // build a precomputed version.
            CharMatcher m1 = is('x');
            assertSame(m1, m1.precomputed());
            assertEquals(m1.toString(), m1.precomputed().toString());
    
            CharMatcher m2 = anyOf("Az");
            assertSame(m2, m2.precomputed());
            assertEquals(m2.toString(), m2.precomputed().toString());
    
            CharMatcher m3 = inRange('A', 'Z');
            assertSame(m3, m3.precomputed());
            assertEquals(m3.toString(), m3.precomputed().toString());
    
            assertSame(CharMatcher.none(), CharMatcher.none().precomputed());
            assertSame(CharMatcher.any(), CharMatcher.any().precomputed());
        }
    
        public void testToString() {
            assertToStringWorks("CharMatcher.none()", anyOf(""));
            assertToStringWorks("CharMatcher.is('\\u0031')", anyOf("1"));
            assertToStringWorks("CharMatcher.isNot('\\u0031')", isNot('1'));
            assertToStringWorks("CharMatcher.anyOf(\"\\u0031\\u0032\")", anyOf("12"));
            assertToStringWorks("CharMatcher.anyOf(\"\\u0031\\u0032\\u0033\")", anyOf("321"));
            assertToStringWorks("CharMatcher.inRange('\\u0031', '\\u0033')", CharMatcher.inRange('1', '3'));
        }
    
        private static void assertToStringWorks(String expected, CharMatcher matcher) {
            assertEquals(expected, matcher.toString());
            assertEquals(expected, matcher.precomputed().toString());
            assertEquals(expected, matcher.negate().negate().toString());
            assertEquals(expected, matcher.negate().precomputed().negate().toString());
            assertEquals(expected, matcher.negate().precomputed().negate().precomputed().toString());
        }
    }

4.Charsets字符集

不要这样做:

    try {
      bytes = string.getBytes("UTF-8");
    } catch (UnsupportedEncodingException e) {
      // how can this possibly happen?
      throw new AssertionError(e);
    }

而是这样做:

    bytes = string.getBytes(Charsets.UTF_8);

Charsets提供了对六个标准Charset实现的常量引用,所有Java平台实现均支持。使用它们而不是通过名称来引用字符集。

(注意:如果使用的是JDK7,则应使用StandardCharsets中的常量。)

4.1使用示例

    import com.google.common.annotations.GwtIncompatible;
    import com.google.common.base.Charsets;
    import junit.framework.TestCase;
    
    import java.nio.charset.Charset;
    import java.util.Arrays;
    
    public class CharsetsTest extends TestCase {
    
        @GwtIncompatible // Non-UTF-8 Charset
        public void testUsAscii() {
            assertEquals(Charset.forName("US-ASCII"), Charsets.US_ASCII);
        }
    
        @GwtIncompatible // Non-UTF-8 Charset
        public void testIso88591() {
            assertEquals(Charset.forName("ISO-8859-1"), Charsets.ISO_8859_1);
        }
    
        public void testUtf8() {
            assertEquals(Charset.forName("UTF-8"), Charsets.UTF_8);
        }
    
        @GwtIncompatible // Non-UTF-8 Charset
        public void testUtf16be() {
            assertEquals(Charset.forName("UTF-16BE"), Charsets.UTF_16BE);
        }
    
        @GwtIncompatible // Non-UTF-8 Charset
        public void testUtf16le() {
            assertEquals(Charset.forName("UTF-16LE"), Charsets.UTF_16LE);
        }
    
        @GwtIncompatible // Non-UTF-8 Charset
        public void testUtf16() {
            assertEquals(Charset.forName("UTF-16"), Charsets.UTF_16);
        }
    
        @GwtIncompatible // Non-UTF-8 Charset
        public void testWhyUsAsciiIsDangerous() {
            byte[] b1 = "朝日新聞".getBytes(Charsets.US_ASCII);
            byte[] b2 = "聞朝日新".getBytes(Charsets.US_ASCII);
            byte[] b3 = "????".getBytes(Charsets.US_ASCII);
            byte[] b4 = "ニュース".getBytes(Charsets.US_ASCII);
            byte[] b5 = "スューー".getBytes(Charsets.US_ASCII);
            // Assert they are all equal (using the transitive property)
            assertTrue(Arrays.equals(b1, b2));
            assertTrue(Arrays.equals(b2, b3));
            assertTrue(Arrays.equals(b3, b4));
            assertTrue(Arrays.equals(b4, b5));
        }
    }

5.Joiner连接器

用分隔符将字符串序列连接在一起可能会很棘手——但事实并非如此。如果你的序列包含null,则可能会更加困难。Joiner的流利风格使其变得简单。

    Joiner joiner = Joiner.on("; ").skipNulls();
    return joiner.join("Harry", null, "Ron", "Hermione");

返回字符串"Harry; Ron; Hermione"。或者,可以使用useForNull(String)指定要使用的字符串代替null,而不是使用skipNulls忽略null。

也可以在对象上使用Joiner,这些对象将使用其toString()进行转换,然后再进行连接。

    Joiner.on(",").join(Arrays.asList(1, 5, 7)); // returns "1,5,7"

警告: 连接器实例始终是不可变的。连接器配置方法始终返回一个新的Joiner连接器,必须使用它来获取所需的语义。这使任何Joiner都是线程安全的,并可用作static final静态最终常量。

5.1使用示例

    import com.google.common.annotations.GwtIncompatible;
    import com.google.common.base.Joiner;
    import com.google.common.base.Joiner.MapJoiner;
    import com.google.common.collect.*;
    import com.google.common.testing.NullPointerTester;
    import junit.framework.AssertionFailedError;
    import junit.framework.TestCase;
    import java.io.IOException;
    import java.util.Arrays;
    import java.util.Iterator;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.Set;
    
    public class JoinerTest extends TestCase {
        private static final Joiner J = Joiner.on("-");
    
        // <Integer> needed to prevent warning :(
        private static final Iterable<Integer> ITERABLE_ = Arrays.<Integer>asList();
        private static final Iterable<Integer> ITERABLE_1 = Arrays.asList(1);
        private static final Iterable<Integer> ITERABLE_12 = Arrays.asList(1, 2);
        private static final Iterable<Integer> ITERABLE_123 = Arrays.asList(1, 2, 3);
        private static final Iterable<Integer> ITERABLE_NULL = Arrays.asList((Integer) null);
        private static final Iterable<Integer> ITERABLE_NULL_NULL = Arrays.asList((Integer) null, null);
        private static final Iterable<Integer> ITERABLE_NULL_1 = Arrays.asList(null, 1);
        private static final Iterable<Integer> ITERABLE_1_NULL = Arrays.asList(1, null);
        private static final Iterable<Integer> ITERABLE_1_NULL_2 = Arrays.asList(1, null, 2);
        private static final Iterable<Integer> ITERABLE_FOUR_NULLS =
                Arrays.asList((Integer) null, null, null, null);
    
        public void testNoSpecialNullBehavior() {
            checkNoOutput(J, ITERABLE_);
            checkResult(J, ITERABLE_1, "1");
            checkResult(J, ITERABLE_12, "1-2");
            checkResult(J, ITERABLE_123, "1-2-3");
    
            try {
                J.join(ITERABLE_NULL);
                fail();
            } catch (NullPointerException expected) {
            }
            try {
                J.join(ITERABLE_1_NULL_2);
                fail();
            } catch (NullPointerException expected) {
            }
    
            try {
                J.join(ITERABLE_NULL.iterator());
                fail();
            } catch (NullPointerException expected) {
            }
            try {
                J.join(ITERABLE_1_NULL_2.iterator());
                fail();
            } catch (NullPointerException expected) {
            }
        }
    
        public void testOnCharOverride() {
            Joiner onChar = Joiner.on('-');
            checkNoOutput(onChar, ITERABLE_);
            checkResult(onChar, ITERABLE_1, "1");
            checkResult(onChar, ITERABLE_12, "1-2");
            checkResult(onChar, ITERABLE_123, "1-2-3");
        }
    
        public void testSkipNulls() {
            Joiner skipNulls = J.skipNulls();
            checkNoOutput(skipNulls, ITERABLE_);
            checkNoOutput(skipNulls, ITERABLE_NULL);
            checkNoOutput(skipNulls, ITERABLE_NULL_NULL);
            checkNoOutput(skipNulls, ITERABLE_FOUR_NULLS);
            checkResult(skipNulls, ITERABLE_1, "1");
            checkResult(skipNulls, ITERABLE_12, "1-2");
            checkResult(skipNulls, ITERABLE_123, "1-2-3");
            checkResult(skipNulls, ITERABLE_NULL_1, "1");
            checkResult(skipNulls, ITERABLE_1_NULL, "1");
            checkResult(skipNulls, ITERABLE_1_NULL_2, "1-2");
        }
    
        public void testUseForNull() {
            Joiner zeroForNull = J.useForNull("0");
            checkNoOutput(zeroForNull, ITERABLE_);
            checkResult(zeroForNull, ITERABLE_1, "1");
            checkResult(zeroForNull, ITERABLE_12, "1-2");
            checkResult(zeroForNull, ITERABLE_123, "1-2-3");
            checkResult(zeroForNull, ITERABLE_NULL, "0");
            checkResult(zeroForNull, ITERABLE_NULL_NULL, "0-0");
            checkResult(zeroForNull, ITERABLE_NULL_1, "0-1");
            checkResult(zeroForNull, ITERABLE_1_NULL, "1-0");
            checkResult(zeroForNull, ITERABLE_1_NULL_2, "1-0-2");
            checkResult(zeroForNull, ITERABLE_FOUR_NULLS, "0-0-0-0");
        }
    
        private static void checkNoOutput(Joiner joiner, Iterable<Integer> set) {
            assertEquals("", joiner.join(set));
            assertEquals("", joiner.join(set.iterator()));
    
            Object[] array = Lists.newArrayList(set).toArray(new Integer[0]);
            assertEquals("", joiner.join(array));
    
            StringBuilder sb1FromIterable = new StringBuilder();
            assertSame(sb1FromIterable, joiner.appendTo(sb1FromIterable, set));
            assertEquals(0, sb1FromIterable.length());
    
            StringBuilder sb1FromIterator = new StringBuilder();
            assertSame(sb1FromIterator, joiner.appendTo(sb1FromIterator, set));
            assertEquals(0, sb1FromIterator.length());
    
            StringBuilder sb2 = new StringBuilder();
            assertSame(sb2, joiner.appendTo(sb2, array));
            assertEquals(0, sb2.length());
    
            try {
                joiner.appendTo(NASTY_APPENDABLE, set);
            } catch (IOException e) {
                throw new AssertionError(e);
            }
    
            try {
                joiner.appendTo(NASTY_APPENDABLE, set.iterator());
            } catch (IOException e) {
                throw new AssertionError(e);
            }
    
            try {
                joiner.appendTo(NASTY_APPENDABLE, array);
            } catch (IOException e) {
                throw new AssertionError(e);
            }
        }
    
        private static final Appendable NASTY_APPENDABLE =
                new Appendable() {
                    @Override
                    public Appendable append(CharSequence csq) throws IOException {
                        throw new IOException();
                    }
    
                    @Override
                    public Appendable append(CharSequence csq, int start, int end) throws IOException {
                        throw new IOException();
                    }
    
                    @Override
                    public Appendable append(char c) throws IOException {
                        throw new IOException();
                    }
                };
    
        private static void checkResult(Joiner joiner, Iterable<Integer> parts, String expected) {
            assertEquals(expected, joiner.join(parts));
            assertEquals(expected, joiner.join(parts.iterator()));
    
            StringBuilder sb1FromIterable = new StringBuilder().append('x');
            joiner.appendTo(sb1FromIterable, parts);
            assertEquals("x" + expected, sb1FromIterable.toString());
    
            StringBuilder sb1FromIterator = new StringBuilder().append('x');
            joiner.appendTo(sb1FromIterator, parts.iterator());
            assertEquals("x" + expected, sb1FromIterator.toString());
    
            Integer[] partsArray = Lists.newArrayList(parts).toArray(new Integer[0]);
            assertEquals(expected, joiner.join(partsArray));
    
            StringBuilder sb2 = new StringBuilder().append('x');
            joiner.appendTo(sb2, partsArray);
            assertEquals("x" + expected, sb2.toString());
    
            int num = partsArray.length - 2;
            if (num >= 0) {
                Object[] rest = new Integer[num];
                for (int i = 0; i < num; i++) {
                    rest[i] = partsArray[i + 2];
                }
    
                assertEquals(expected, joiner.join(partsArray[0], partsArray[1], rest));
    
                StringBuilder sb3 = new StringBuilder().append('x');
                joiner.appendTo(sb3, partsArray[0], partsArray[1], rest);
                assertEquals("x" + expected, sb3.toString());
            }
        }
    
        public void test_useForNull_skipNulls() {
            Joiner j = Joiner.on("x").useForNull("y");
            try {
                j = j.skipNulls();
                fail();
            } catch (UnsupportedOperationException expected) {
            }
        }
    
        public void test_skipNulls_useForNull() {
            Joiner j = Joiner.on("x").skipNulls();
            try {
                j = j.useForNull("y");
                fail();
            } catch (UnsupportedOperationException expected) {
            }
        }
    
        public void test_useForNull_twice() {
            Joiner j = Joiner.on("x").useForNull("y");
            try {
                j = j.useForNull("y");
                fail();
            } catch (UnsupportedOperationException expected) {
            }
        }
    
        public void testMap() {
            MapJoiner j = Joiner.on(';').withKeyValueSeparator(':');
            assertEquals("", j.join(ImmutableMap.of()));
            assertEquals(":", j.join(ImmutableMap.of("", "")));
    
            Map<String, String> mapWithNulls = Maps.newLinkedHashMap();
            mapWithNulls.put("a", null);
            mapWithNulls.put(null, "b");
    
            try {
                j.join(mapWithNulls);
                fail();
            } catch (NullPointerException expected) {
            }
    
            assertEquals("a:00;00:b", j.useForNull("00").join(mapWithNulls));
    
            StringBuilder sb = new StringBuilder();
            j.appendTo(sb, ImmutableMap.of(1, 2, 3, 4, 5, 6));
            assertEquals("1:2;3:4;5:6", sb.toString());
        }
    
        public void testEntries() {
            MapJoiner j = Joiner.on(";").withKeyValueSeparator(":");
            assertEquals("", j.join(ImmutableMultimap.of().entries()));
            assertEquals("", j.join(ImmutableMultimap.of().entries().iterator()));
            assertEquals(":", j.join(ImmutableMultimap.of("", "").entries()));
            assertEquals(":", j.join(ImmutableMultimap.of("", "").entries().iterator()));
            assertEquals("1:a;1:b", j.join(ImmutableMultimap.of("1", "a", "1", "b").entries()));
            assertEquals("1:a;1:b", j.join(ImmutableMultimap.of("1", "a", "1", "b").entries().iterator()));
    
            Map<String, String> mapWithNulls = Maps.newLinkedHashMap();
            mapWithNulls.put("a", null);
            mapWithNulls.put(null, "b");
            Set<Entry<String, String>> entriesWithNulls = mapWithNulls.entrySet();
    
            try {
                j.join(entriesWithNulls);
                fail();
            } catch (NullPointerException expected) {
            }
    
            try {
                j.join(entriesWithNulls.iterator());
                fail();
            } catch (NullPointerException expected) {
            }
    
            assertEquals("a:00;00:b", j.useForNull("00").join(entriesWithNulls));
            assertEquals("a:00;00:b", j.useForNull("00").join(entriesWithNulls.iterator()));
    
            StringBuilder sb1 = new StringBuilder();
            j.appendTo(sb1, ImmutableMultimap.of(1, 2, 3, 4, 5, 6, 1, 3, 5, 10).entries());
            assertEquals("1:2;1:3;3:4;5:6;5:10", sb1.toString());
    
            StringBuilder sb2 = new StringBuilder();
            j.appendTo(sb2, ImmutableMultimap.of(1, 2, 3, 4, 5, 6, 1, 3, 5, 10).entries().iterator());
            assertEquals("1:2;1:3;3:4;5:6;5:10", sb2.toString());
        }
    
        public void test_skipNulls_onMap() {
            Joiner j = Joiner.on(",").skipNulls();
            try {
                j.withKeyValueSeparator("/");
                fail();
            } catch (UnsupportedOperationException expected) {
            }
        }
    
        private static class DontStringMeBro implements CharSequence {
            @Override
            public int length() {
                return 3;
            }
    
            @Override
            public char charAt(int index) {
                return "foo".charAt(index);
            }
    
            @Override
            public CharSequence subSequence(int start, int end) {
                return "foo".subSequence(start, end);
            }
    
            @Override
            public String toString() {
                throw new AssertionFailedError("shouldn't be invoked");
            }
        }
    
        // Don't do this.
        private static class IterableIterator implements Iterable<Integer>, Iterator<Integer> {
            private static final ImmutableSet<Integer> INTEGERS = ImmutableSet.of(1, 2, 3, 4);
            private final Iterator<Integer> iterator;
    
            public IterableIterator() {
                this.iterator = iterator();
            }
    
            @Override
            public Iterator<Integer> iterator() {
                return INTEGERS.iterator();
            }
    
            @Override
            public boolean hasNext() {
                return iterator.hasNext();
            }
    
            @Override
            public Integer next() {
                return iterator.next();
            }
    
            @Override
            public void remove() {
                iterator.remove();
            }
        }
    
        @GwtIncompatible // StringBuilder.append in GWT invokes Object.toString(), unlike the JRE version.
        public void testDontConvertCharSequenceToString() {
            assertEquals("foo,foo", Joiner.on(",").join(new DontStringMeBro(), new DontStringMeBro()));
            assertEquals(
            	"foo,bar,foo",
                Joiner.on(",").useForNull("bar").join(new DontStringMeBro(), null, new DontStringMeBro()));
        }
    
        @GwtIncompatible // NullPointerTester
        public void testNullPointers() {
            NullPointerTester tester = new NullPointerTester();
            tester.testAllPublicStaticMethods(Joiner.class);
            tester.testInstanceMethods(Joiner.on(","), NullPointerTester.Visibility.PACKAGE);
            tester.testInstanceMethods(Joiner.on(",").skipNulls(), NullPointerTester.Visibility.PACKAGE);
            tester.testInstanceMethods(
                    Joiner.on(",").useForNull("x"), NullPointerTester.Visibility.PACKAGE);
            tester.testInstanceMethods(
                    Joiner.on(",").withKeyValueSeparator("="), NullPointerTester.Visibility.PACKAGE);
        }
    }

6.Splitter拆分器

用于拆分字符串的内置Java工具具有一些古怪的行为。例如,String.split会静默丢弃尾部的分隔符,而StringTokenizer只考虑五个空格字符而不考虑其它字符。

测验:",a,,b,".split(",")返回什么?

  1. "", "a", "", "b", ""
  2. null, "a", null, "b", null
  3. "a", null, "b"
  4. "a", "b"
  5. 不是上述任何一个

正确的答案不是上述任何一个"", "a", "", "b"。只跳过尾部的空字符串。

Splitter拆分器使用令人放心的直接流利的模式,可以完全控制所有这些令人困惑的行为。

    Splitter.on(',')
        .trimResults()
        .omitEmptyStrings()
        .split("foo,bar,,   qux");

返回包含"foo", “bar”, "qux"的Iterable<String>Splitter拆分器可以设置在任何PatterncharStringCharMatcher上拆分。

6.1基本工厂

方法 描述 示例
Splitter.on(char) 根据出现的特定单个字符进行拆分。 Splitter.on(';')
Splitter.on(CharMatcher) 根据出现的某个类别中任意字符进行拆分。 Splitter.on(CharMatcher.BREAKING_WHITESPACE)Splitter.on(CharMatcher.anyOf(";,."))
Splitter.on(String) 在String字符串上分割。 Splitter.on(",")
Splitter.on(Pattern)Splitter.onPattern(String) 按正则表达式拆分。 Splitter.onPattern("\r?\n")
Splitter.fixedLength(int) 将字符串拆分为指定固定长度的子字符串。最后一项可以小于length长度,但永远不会为空。 Splitter.fixedLength(3)

6.2修饰符

方法 描述 示例
omitEmptyStrings() 自动从结果中删除空字符串。 Splitter.on(',').omitEmptyStrings().split("a,,c,d")returns"a","c","d"
trimResults() 从结果中修剪空格;等效于trimResults(CharMatcher.WHITESPACE). Splitter.on(',').trimResults().split("a,b,c,d")returns"a","b","c","d"
trimResults(CharMatcher) 从结果中修剪与指定CharMatcher匹配的字符。 Splitter.on(',').trimResults(CharMatcher.is('')).split("a,b,c_")returns"a","b","c".
limit(int) 返回指定数量的字符串后停止拆分。 Splitter.on(',').limit(3).split("a,b,c,d")returns"a","b","c,d"

如果希望获取List列表,请使用splitToList()代替split()

警告: 拆分器实例始终是不可变的。拆分器配置方法将始终返回新的Splitter拆分器,必须使用它来获取所需的语义。这样可以确保任何Splitter线程安全,并可用作static final静态最终常量。

6.3Map拆分器

还可以使用拆分器通过使用withKeyValueSeparator()指定第二个分隔符对map进行反序列化。生成的MapSplitter 将使用拆分器的分隔符将输入拆分为条目,然后使用给定的键值分隔符将这些条目拆分为键和值,并返回Map<String, String>

6.4使用示例

    import com.google.common.annotations.GwtIncompatible;
    import com.google.common.base.CharMatcher;
    import com.google.common.base.Splitter;
    import com.google.common.base.Splitter.MapSplitter;
    import com.google.common.collect.ImmutableMap;
    import com.google.common.testing.NullPointerTester;
    import junit.framework.TestCase;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Map;
    import java.util.regex.Pattern;
    import static com.google.common.collect.ImmutableList.toImmutableList;
    import static com.google.common.truth.Truth.assertThat;
    
    public class SplitterTest extends TestCase {
    
        private static final Splitter COMMA_SPLITTER = Splitter.on(',');
    
        public void testSplitNullString() {
            try {
                COMMA_SPLITTER.split(null);
                fail();
            } catch (NullPointerException expected) {
            }
        }
    
        public void testCharacterSimpleSplit() {
            String simple = "a,b,c";
            Iterable<String> letters = COMMA_SPLITTER.split(simple);
            assertThat(letters).containsExactly("a", "b", "c").inOrder();
        }
        
        public void testCharacterSimpleSplitToList() {
            String simple = "a,b,c";
            List<String> letters = COMMA_SPLITTER.splitToList(simple);
            assertThat(letters).containsExactly("a", "b", "c").inOrder();
        }
    
        public void testCharacterSimpleSplitToStream() {
            String simple = "a,b,c";
            List<String> letters = COMMA_SPLITTER.splitToStream(simple).collect(toImmutableList());
            assertThat(letters).containsExactly("a", "b", "c").inOrder();
        }
    
        public void testToString() {
            assertEquals("[]", COMMA_SPLITTER.split("").toString());
            assertEquals("[a, b, c]", COMMA_SPLITTER.split("a,b,c").toString());
            assertEquals("[yam, bam, jam, ham]", Splitter.on(", ").split("yam, bam, jam, ham").toString());
        }
    
        public void testCharacterSimpleSplitWithNoDelimiter() {
            String simple = "a,b,c";
            Iterable<String> letters = Splitter.on('.').split(simple);
            assertThat(letters).containsExactly("a,b,c").inOrder();
        }
    
        public void testCharacterSplitWithDoubleDelimiter() {
            String doubled = "a,,b,c";
            Iterable<String> letters = COMMA_SPLITTER.split(doubled);
            assertThat(letters).containsExactly("a", "", "b", "c").inOrder();
        }
    
        public void testCharacterSplitWithDoubleDelimiterAndSpace() {
            String doubled = "a,, b,c";
            Iterable<String> letters = COMMA_SPLITTER.split(doubled);
            assertThat(letters).containsExactly("a", "", " b", "c").inOrder();
        }
    
        public void testCharacterSplitWithTrailingDelimiter() {
            String trailing = "a,b,c,";
            Iterable<String> letters = COMMA_SPLITTER.split(trailing);
            assertThat(letters).containsExactly("a", "b", "c", "").inOrder();
        }
    
        public void testCharacterSplitWithLeadingDelimiter() {
            String leading = ",a,b,c";
            Iterable<String> letters = COMMA_SPLITTER.split(leading);
            assertThat(letters).containsExactly("", "a", "b", "c").inOrder();
        }
    
        public void testCharacterSplitWithMultipleLetters() {
            Iterable<String> testCharacteringMotto =
                    Splitter.on('-').split("Testing-rocks-Debugging-sucks");
            assertThat(testCharacteringMotto)
                    .containsExactly("Testing", "rocks", "Debugging", "sucks")
                    .inOrder();
        }
    
        public void testCharacterSplitWithMatcherDelimiter() {
            Iterable<String> testCharacteringMotto =
                    Splitter.on(CharMatcher.whitespace()).split("Testing\nrocks\tDebugging sucks");
            assertThat(testCharacteringMotto)
                    .containsExactly("Testing", "rocks", "Debugging", "sucks")
                    .inOrder();
        }
    
        public void testCharacterSplitWithDoubleDelimiterOmitEmptyStrings() {
            String doubled = "a..b.c";
            Iterable<String> letters = Splitter.on('.').omitEmptyStrings().split(doubled);
            assertThat(letters).containsExactly("a", "b", "c").inOrder();
        }
    
        public void testCharacterSplitEmptyToken() {
            String emptyToken = "a. .c";
            Iterable<String> letters = Splitter.on('.').trimResults().split(emptyToken);
            assertThat(letters).containsExactly("a", "", "c").inOrder();
        }
    
        public void testCharacterSplitEmptyTokenOmitEmptyStrings() {
            String emptyToken = "a. .c";
            Iterable<String> letters = Splitter.on('.').omitEmptyStrings().trimResults().split(emptyToken);
            assertThat(letters).containsExactly("a", "c").inOrder();
        }
    
        public void testCharacterSplitOnEmptyString() {
            Iterable<String> nothing = Splitter.on('.').split("");
            assertThat(nothing).containsExactly("").inOrder();
        }
    
        public void testCharacterSplitOnEmptyStringOmitEmptyStrings() {
            assertThat(Splitter.on('.').omitEmptyStrings().split("")).isEmpty();
        }
    
        public void testCharacterSplitOnOnlyDelimiter() {
            Iterable<String> blankblank = Splitter.on('.').split(".");
            assertThat(blankblank).containsExactly("", "").inOrder();
        }
    
        public void testCharacterSplitOnOnlyDelimitersOmitEmptyStrings() {
            Iterable<String> empty = Splitter.on('.').omitEmptyStrings().split("...");
            assertThat(empty).isEmpty();
        }
    
        public void testCharacterSplitWithTrim() {
            String jacksons =
                    "arfo(Marlon)aorf, (Michael)orfa, afro(Jackie)orfa, " + "ofar(Jemaine), aff(Tito)";
            Iterable<String> family =
                    COMMA_SPLITTER
                    	.trimResults(CharMatcher.anyOf("afro").or(CharMatcher.whitespace()))
                        .split(jacksons);
            assertThat(family)
                    .containsExactly("(Marlon)", "(Michael)", "(Jackie)", "(Jemaine)", "(Tito)")
                    .inOrder();
        }
    
        public void testStringSimpleSplit() {
            String simple = "a,b,c";
            Iterable<String> letters = Splitter.on(",").split(simple);
            assertThat(letters).containsExactly("a", "b", "c").inOrder();
        }
    
        public void testStringSimpleSplitWithNoDelimiter() {
            String simple = "a,b,c";
            Iterable<String> letters = Splitter.on(".").split(simple);
            assertThat(letters).containsExactly("a,b,c").inOrder();
        }
    
        public void testStringSplitWithDoubleDelimiter() {
            String doubled = "a,,b,c";
            Iterable<String> letters = Splitter.on(",").split(doubled);
            assertThat(letters).containsExactly("a", "", "b", "c").inOrder();
        }
    
        public void testStringSplitWithDoubleDelimiterAndSpace() {
            String doubled = "a,, b,c";
            Iterable<String> letters = Splitter.on(",").split(doubled);
            assertThat(letters).containsExactly("a", "", " b", "c").inOrder();
        }
    
        public void testStringSplitWithTrailingDelimiter() {
            String trailing = "a,b,c,";
            Iterable<String> letters = Splitter.on(",").split(trailing);
            assertThat(letters).containsExactly("a", "b", "c", "").inOrder();
        }
    
        public void testStringSplitWithLeadingDelimiter() {
            String leading = ",a,b,c";
            Iterable<String> letters = Splitter.on(",").split(leading);
            assertThat(letters).containsExactly("", "a", "b", "c").inOrder();
        }
    
        public void testStringSplitWithMultipleLetters() {
            Iterable<String> testStringingMotto = Splitter.on("-").split("Testing-rocks-Debugging-sucks");
            assertThat(testStringingMotto)
                    .containsExactly("Testing", "rocks", "Debugging", "sucks")
                    .inOrder();
        }
    
        public void testStringSplitWithDoubleDelimiterOmitEmptyStrings() {
            String doubled = "a..b.c";
            Iterable<String> letters = Splitter.on(".").omitEmptyStrings().split(doubled);
            assertThat(letters).containsExactly("a", "b", "c").inOrder();
        }
    
        public void testStringSplitEmptyToken() {
            String emptyToken = "a. .c";
            Iterable<String> letters = Splitter.on(".").trimResults().split(emptyToken);
            assertThat(letters).containsExactly("a", "", "c").inOrder();
        }
    
        public void testStringSplitEmptyTokenOmitEmptyStrings() {
            String emptyToken = "a. .c";
            Iterable<String> letters = Splitter.on(".").omitEmptyStrings().trimResults().split(emptyToken);
            assertThat(letters).containsExactly("a", "c").inOrder();
        }
    
        public void testStringSplitWithLongDelimiter() {
            String longDelimiter = "a, b, c";
            Iterable<String> letters = Splitter.on(", ").split(longDelimiter);
            assertThat(letters).containsExactly("a", "b", "c").inOrder();
        }
    
        public void testStringSplitWithLongLeadingDelimiter() {
            String longDelimiter = ", a, b, c";
            Iterable<String> letters = Splitter.on(", ").split(longDelimiter);
            assertThat(letters).containsExactly("", "a", "b", "c").inOrder();
        }
    
        public void testStringSplitWithLongTrailingDelimiter() {
            String longDelimiter = "a, b, c, ";
            Iterable<String> letters = Splitter.on(", ").split(longDelimiter);
            assertThat(letters).containsExactly("a", "b", "c", "").inOrder();
        }
    
        public void testStringSplitWithDelimiterSubstringInValue() {
            String fourCommasAndFourSpaces = ",,,,    ";
            Iterable<String> threeCommasThenThreeSpaces = Splitter.on(", ").split(fourCommasAndFourSpaces);
            assertThat(threeCommasThenThreeSpaces).containsExactly(",,,", "   ").inOrder();
        }
    
        public void testStringSplitWithEmptyString() {
            try {
                Splitter.on("");
                fail();
            } catch (IllegalArgumentException expected) {
            }
        }
    
        public void testStringSplitOnEmptyString() {
            Iterable<String> notMuch = Splitter.on(".").split("");
            assertThat(notMuch).containsExactly("").inOrder();
        }
    
        public void testStringSplitOnEmptyStringOmitEmptyString() {
            assertThat(Splitter.on(".").omitEmptyStrings().split("")).isEmpty();
        }
    
        public void testStringSplitOnOnlyDelimiter() {
            Iterable<String> blankblank = Splitter.on(".").split(".");
            assertThat(blankblank).containsExactly("", "").inOrder();
        }
    
        public void testStringSplitOnOnlyDelimitersOmitEmptyStrings() {
            Iterable<String> empty = Splitter.on(".").omitEmptyStrings().split("...");
            assertThat(empty).isEmpty();
        }
    
        public void testStringSplitWithTrim() {
            String jacksons =
                    "arfo(Marlon)aorf, (Michael)orfa, afro(Jackie)orfa, " + "ofar(Jemaine), aff(Tito)";
            Iterable<String> family =
                    Splitter.on(",")
                            .trimResults(CharMatcher.anyOf("afro").or(CharMatcher.whitespace()))
                            .split(jacksons);
            assertThat(family)
                    .containsExactly("(Marlon)", "(Michael)", "(Jackie)", "(Jemaine)", "(Tito)")
                    .inOrder();
        }
    
        @GwtIncompatible // Splitter.onPattern
        public void testPatternSimpleSplit() {
            String simple = "a,b,c";
            Iterable<String> letters = Splitter.onPattern(",").split(simple);
            assertThat(letters).containsExactly("a", "b", "c").inOrder();
        }
    
        @GwtIncompatible // Splitter.onPattern
        public void testPatternSimpleSplitWithNoDelimiter() {
            String simple = "a,b,c";
            Iterable<String> letters = Splitter.onPattern("foo").split(simple);
            assertThat(letters).containsExactly("a,b,c").inOrder();
        }
    
        @GwtIncompatible // Splitter.onPattern
        public void testPatternSplitWithDoubleDelimiter() {
            String doubled = "a,,b,c";
            Iterable<String> letters = Splitter.onPattern(",").split(doubled);
            assertThat(letters).containsExactly("a", "", "b", "c").inOrder();
        }
    
        @GwtIncompatible // Splitter.onPattern
        public void testPatternSplitWithDoubleDelimiterAndSpace() {
            String doubled = "a,, b,c";
            Iterable<String> letters = Splitter.onPattern(",").split(doubled);
            assertThat(letters).containsExactly("a", "", " b", "c").inOrder();
        }
    
        @GwtIncompatible // Splitter.onPattern
        public void testPatternSplitWithTrailingDelimiter() {
            String trailing = "a,b,c,";
            Iterable<String> letters = Splitter.onPattern(",").split(trailing);
            assertThat(letters).containsExactly("a", "b", "c", "").inOrder();
        }
    
        @GwtIncompatible // Splitter.onPattern
        public void testPatternSplitWithLeadingDelimiter() {
            String leading = ",a,b,c";
            Iterable<String> letters = Splitter.onPattern(",").split(leading);
            assertThat(letters).containsExactly("", "a", "b", "c").inOrder();
        }
    
        @GwtIncompatible // Splitter.onPattern
        public void testPatternSplitWithMultipleLetters() {
            Iterable<String> testPatterningMotto =
                    Splitter.onPattern("-").split("Testing-rocks-Debugging-sucks");
            assertThat(testPatterningMotto)
                    .containsExactly("Testing", "rocks", "Debugging", "sucks")
                    .inOrder();
        }
    
        @GwtIncompatible // java.util.regex.Pattern
        private static Pattern literalDotPattern() {
            return Pattern.compile("\\.");
        }
    
        @GwtIncompatible // java.util.regex.Pattern
        public void testPatternSplitWithDoubleDelimiterOmitEmptyStrings() {
            String doubled = "a..b.c";
            Iterable<String> letters = Splitter.on(literalDotPattern()).omitEmptyStrings().split(doubled);
            assertThat(letters).containsExactly("a", "b", "c").inOrder();
        }
    
        @GwtIncompatible // java.util.regex.Pattern
        public void testPatternSplitWordBoundary() {
            String string = "foo<bar>bletch";
            Iterable<String> words = Splitter.on(Pattern.compile("\\b")).split(string);
            assertThat(words).containsExactly("foo", "<", "bar", ">", "bletch").inOrder();
        }
    
        @GwtIncompatible // java.util.regex.Pattern
        public void testPatternSplitWordBoundary_singleCharInput() {
            String string = "f";
            Iterable<String> words = Splitter.on(Pattern.compile("\\b")).split(string);
            assertThat(words).containsExactly("f").inOrder();
        }
    
        @GwtIncompatible // java.util.regex.Pattern
        public void testPatternSplitWordBoundary_singleWordInput() {
            String string = "foo";
            Iterable<String> words = Splitter.on(Pattern.compile("\\b")).split(string);
            assertThat(words).containsExactly("foo").inOrder();
        }
    
        @GwtIncompatible // java.util.regex.Pattern
        public void testPatternSplitEmptyToken() {
            String emptyToken = "a. .c";
            Iterable<String> letters = Splitter.on(literalDotPattern()).trimResults().split(emptyToken);
            assertThat(letters).containsExactly("a", "", "c").inOrder();
        }
    
        @GwtIncompatible // java.util.regex.Pattern
        public void testPatternSplitEmptyTokenOmitEmptyStrings() {
            String emptyToken = "a. .c";
            Iterable<String> letters =
                    Splitter.on(literalDotPattern()).omitEmptyStrings().trimResults().split(emptyToken);
            assertThat(letters).containsExactly("a", "c").inOrder();
        }
    
        @GwtIncompatible // java.util.regex.Pattern
        public void testPatternSplitOnOnlyDelimiter() {
            Iterable<String> blankblank = Splitter.on(literalDotPattern()).split(".");
    
            assertThat(blankblank).containsExactly("", "").inOrder();
        }
    
        @GwtIncompatible // java.util.regex.Pattern
        public void testPatternSplitOnOnlyDelimitersOmitEmptyStrings() {
            Iterable<String> empty = Splitter.on(literalDotPattern()).omitEmptyStrings().split("...");
            assertThat(empty).isEmpty();
        }
    
        @GwtIncompatible // java.util.regex.Pattern
        public void testPatternSplitMatchingIsGreedy() {
            String longDelimiter = "a, b,   c";
            Iterable<String> letters = Splitter.on(Pattern.compile(",\\s*")).split(longDelimiter);
            assertThat(letters).containsExactly("a", "b", "c").inOrder();
        }
    
        @GwtIncompatible // java.util.regex.Pattern
        public void testPatternSplitWithLongLeadingDelimiter() {
            String longDelimiter = ", a, b, c";
            Iterable<String> letters = Splitter.on(Pattern.compile(", ")).split(longDelimiter);
            assertThat(letters).containsExactly("", "a", "b", "c").inOrder();
        }
    
        @GwtIncompatible // java.util.regex.Pattern
        public void testPatternSplitWithLongTrailingDelimiter() {
            String longDelimiter = "a, b, c/ ";
            Iterable<String> letters = Splitter.on(Pattern.compile("[,/]\\s")).split(longDelimiter);
            assertThat(letters).containsExactly("a", "b", "c", "").inOrder();
        }
    
        @GwtIncompatible // java.util.regex.Pattern
        public void testPatternSplitInvalidPattern() {
            try {
                Splitter.on(Pattern.compile("a*"));
                fail();
            } catch (IllegalArgumentException expected) {
            }
        }
    
        @GwtIncompatible // java.util.regex.Pattern
        public void testPatternSplitWithTrim() {
            String jacksons =
                    "arfo(Marlon)aorf, (Michael)orfa, afro(Jackie)orfa, " + "ofar(Jemaine), aff(Tito)";
            Iterable<String> family =
                    Splitter.on(Pattern.compile(","))
                            .trimResults(CharMatcher.anyOf("afro").or(CharMatcher.whitespace()))
                            .split(jacksons);
            assertThat(family)
                    .containsExactly("(Marlon)", "(Michael)", "(Jackie)", "(Jemaine)", "(Tito)")
                    .inOrder();
        }
    
        public void testSplitterIterableIsUnmodifiable_char() {
            assertIteratorIsUnmodifiable(COMMA_SPLITTER.split("a,b").iterator());
        }
    
        public void testSplitterIterableIsUnmodifiable_string() {
            assertIteratorIsUnmodifiable(Splitter.on(",").split("a,b").iterator());
        }
    
        @GwtIncompatible // java.util.regex.Pattern
        public void testSplitterIterableIsUnmodifiable_pattern() {
            assertIteratorIsUnmodifiable(Splitter.on(Pattern.compile(",")).split("a,b").iterator());
        }
    
        private void assertIteratorIsUnmodifiable(Iterator<?> iterator) {
            iterator.next();
            try {
                iterator.remove();
                fail();
            } catch (UnsupportedOperationException expected) {
            }
        }
    
        public void testSplitterIterableIsLazy_char() {
            assertSplitterIterableIsLazy(COMMA_SPLITTER);
        }
    
        public void testSplitterIterableIsLazy_string() {
            assertSplitterIterableIsLazy(Splitter.on(","));
        }
    
        /**
         * This test really pushes the boundaries of what we support. In general the splitter's behaviour
         * is not well defined if the char sequence it's splitting is mutated during iteration.
         */
        private void assertSplitterIterableIsLazy(Splitter splitter) {
            StringBuilder builder = new StringBuilder();
            Iterator<String> iterator = splitter.split(builder).iterator();
    
            builder.append("A,");
            assertEquals("A", iterator.next());
            builder.append("B,");
            assertEquals("B", iterator.next());
            builder.append("C");
            assertEquals("C", iterator.next());
            assertFalse(iterator.hasNext());
        }
    
        public void testFixedLengthSimpleSplit() {
            String simple = "abcde";
            Iterable<String> letters = Splitter.fixedLength(2).split(simple);
            assertThat(letters).containsExactly("ab", "cd", "e").inOrder();
        }
    
        public void testFixedLengthSplitEqualChunkLength() {
            String simple = "abcdef";
            Iterable<String> letters = Splitter.fixedLength(2).split(simple);
            assertThat(letters).containsExactly("ab", "cd", "ef").inOrder();
        }
    
        public void testFixedLengthSplitOnlyOneChunk() {
            String simple = "abc";
            Iterable<String> letters = Splitter.fixedLength(3).split(simple);
            assertThat(letters).containsExactly("abc").inOrder();
        }
    
        public void testFixedLengthSplitSmallerString() {
            String simple = "ab";
            Iterable<String> letters = Splitter.fixedLength(3).split(simple);
            assertThat(letters).containsExactly("ab").inOrder();
        }
    
        public void testFixedLengthSplitEmptyString() {
            String simple = "";
            Iterable<String> letters = Splitter.fixedLength(3).split(simple);
            assertThat(letters).containsExactly("").inOrder();
        }
    
        public void testFixedLengthSplitEmptyStringWithOmitEmptyStrings() {
            assertThat(Splitter.fixedLength(3).omitEmptyStrings().split("")).isEmpty();
        }
    
        public void testFixedLengthSplitIntoChars() {
            String simple = "abcd";
            Iterable<String> letters = Splitter.fixedLength(1).split(simple);
            assertThat(letters).containsExactly("a", "b", "c", "d").inOrder();
        }
    
        public void testFixedLengthSplitZeroChunkLen() {
            try {
                Splitter.fixedLength(0);
                fail();
            } catch (IllegalArgumentException expected) {
            }
        }
    
        public void testFixedLengthSplitNegativeChunkLen() {
            try {
                Splitter.fixedLength(-1);
                fail();
            } catch (IllegalArgumentException expected) {
            }
        }
    
        public void testLimitLarge() {
            String simple = "abcd";
            Iterable<String> letters = Splitter.fixedLength(1).limit(100).split(simple);
            assertThat(letters).containsExactly("a", "b", "c", "d").inOrder();
        }
    
        public void testLimitOne() {
            String simple = "abcd";
            Iterable<String> letters = Splitter.fixedLength(1).limit(1).split(simple);
            assertThat(letters).containsExactly("abcd").inOrder();
        }
    
        public void testLimitFixedLength() {
            String simple = "abcd";
            Iterable<String> letters = Splitter.fixedLength(1).limit(2).split(simple);
            assertThat(letters).containsExactly("a", "bcd").inOrder();
        }
    
        public void testLimit1Separator() {
            String simple = "a,b,c,d";
            Iterable<String> items = COMMA_SPLITTER.limit(1).split(simple);
            assertThat(items).containsExactly("a,b,c,d").inOrder();
        }
    
        public void testLimitSeparator() {
            String simple = "a,b,c,d";
            Iterable<String> items = COMMA_SPLITTER.limit(2).split(simple);
            assertThat(items).containsExactly("a", "b,c,d").inOrder();
        }
    
        public void testLimitExtraSeparators() {
            String text = "a,,,b,,c,d";
            Iterable<String> items = COMMA_SPLITTER.limit(2).split(text);
            assertThat(items).containsExactly("a", ",,b,,c,d").inOrder();
        }
    
        public void testLimitExtraSeparatorsOmitEmpty() {
            String text = "a,,,b,,c,d";
            Iterable<String> items = COMMA_SPLITTER.limit(2).omitEmptyStrings().split(text);
            assertThat(items).containsExactly("a", "b,,c,d").inOrder();
        }
    
        public void testLimitExtraSeparatorsOmitEmpty3() {
            String text = "a,,,b,,c,d";
            Iterable<String> items = COMMA_SPLITTER.limit(3).omitEmptyStrings().split(text);
            assertThat(items).containsExactly("a", "b", "c,d").inOrder();
        }
    
        public void testLimitExtraSeparatorsTrim() {
            String text = ",,a,,  , b ,, c,d ";
            Iterable<String> items = COMMA_SPLITTER.limit(2).omitEmptyStrings().trimResults().split(text);
            assertThat(items).containsExactly("a", "b ,, c,d").inOrder();
        }
    
        public void testLimitExtraSeparatorsTrim3() {
            String text = ",,a,,  , b ,, c,d ";
            Iterable<String> items = COMMA_SPLITTER.limit(3).omitEmptyStrings().trimResults().split(text);
            assertThat(items).containsExactly("a", "b", "c,d").inOrder();
        }
    
        public void testLimitExtraSeparatorsTrim1() {
            String text = ",,a,,  , b ,, c,d ";
            Iterable<String> items = COMMA_SPLITTER.limit(1).omitEmptyStrings().trimResults().split(text);
            assertThat(items).containsExactly("a,,  , b ,, c,d").inOrder();
        }
    
        public void testLimitExtraSeparatorsTrim1NoOmit() {
            String text = ",,a,,  , b ,, c,d ";
            Iterable<String> items = COMMA_SPLITTER.limit(1).trimResults().split(text);
            assertThat(items).containsExactly(",,a,,  , b ,, c,d").inOrder();
        }
    
        public void testLimitExtraSeparatorsTrim1Empty() {
            String text = "";
            Iterable<String> items = COMMA_SPLITTER.limit(1).split(text);
            assertThat(items).containsExactly("").inOrder();
        }
    
        public void testLimitExtraSeparatorsTrim1EmptyOmit() {
            String text = "";
            Iterable<String> items = COMMA_SPLITTER.omitEmptyStrings().limit(1).split(text);
            assertThat(items).isEmpty();
        }
    
        public void testInvalidZeroLimit() {
            try {
                COMMA_SPLITTER.limit(0);
                fail();
            } catch (IllegalArgumentException expected) {
            }
        }
    
        @GwtIncompatible // NullPointerTester
        public void testNullPointers() {
            NullPointerTester tester = new NullPointerTester();
            tester.testAllPublicStaticMethods(Splitter.class);
            tester.testAllPublicInstanceMethods(COMMA_SPLITTER);
            tester.testAllPublicInstanceMethods(COMMA_SPLITTER.trimResults());
        }
    
        public void testMapSplitter_trimmedBoth() {
            Map<String, String> m =
                    COMMA_SPLITTER
                            .trimResults()
                            .withKeyValueSeparator(Splitter.on(':').trimResults())
                            .split("boy  : tom , girl: tina , cat  : kitty , dog: tommy ");
            ImmutableMap<String, String> expected =
                    ImmutableMap.of("boy", "tom", "girl", "tina", "cat", "kitty", "dog", "tommy");
            assertThat(m).isEqualTo(expected);
            assertThat(m.entrySet()).containsExactlyElementsIn(expected.entrySet()).inOrder();
        }
    
        public void testMapSplitter_trimmedEntries() {
            Map<String, String> m =
                    COMMA_SPLITTER
                            .trimResults()
                            .withKeyValueSeparator(":")
                            .split("boy  : tom , girl: tina , cat  : kitty , dog: tommy ");
            ImmutableMap<String, String> expected =
                    ImmutableMap.of("boy  ", " tom", "girl", " tina", "cat  ", " kitty", "dog", " tommy");
    
            assertThat(m).isEqualTo(expected);
            assertThat(m.entrySet()).containsExactlyElementsIn(expected.entrySet()).inOrder();
        }
    
        public void testMapSplitter_trimmedKeyValue() {
            Map<String, String> m =
                    COMMA_SPLITTER
                            .withKeyValueSeparator(Splitter.on(':').trimResults())
                            .split("boy  : tom , girl: tina , cat  : kitty , dog: tommy ");
            ImmutableMap<String, String> expected =
                    ImmutableMap.of("boy", "tom", "girl", "tina", "cat", "kitty", "dog", "tommy");
            assertThat(m).isEqualTo(expected);
            assertThat(m.entrySet()).containsExactlyElementsIn(expected.entrySet()).inOrder();
        }
    
        public void testMapSplitter_notTrimmed() {
            Map<String, String> m =
                    COMMA_SPLITTER
                            .withKeyValueSeparator(":")
                            .split(" boy:tom , girl: tina , cat :kitty , dog:  tommy ");
            ImmutableMap<String, String> expected =
            	ImmutableMap.of(" boy", "tom ", " girl", " tina ", " cat ", "kitty ", " dog", "  tommy ");
            assertThat(m).isEqualTo(expected);
            assertThat(m.entrySet()).containsExactlyElementsIn(expected.entrySet()).inOrder();
        }
    
        public void testMapSplitter_CharacterSeparator() {
            // try different delimiters.
            Map<String, String> m =
            	Splitter.on(",").withKeyValueSeparator(':').split("boy:tom,girl:tina,cat:kitty,dog:tommy");
            ImmutableMap<String, String> expected =
                    ImmutableMap.of("boy", "tom", "girl", "tina", "cat", "kitty", "dog", "tommy");
    
            assertThat(m).isEqualTo(expected);
            assertThat(m.entrySet()).containsExactlyElementsIn(expected.entrySet()).inOrder();
        }
    
        public void testMapSplitter_multiCharacterSeparator() {
            // try different delimiters.
            Map<String, String> m =
                    Splitter.on(",")
                            .withKeyValueSeparator(":^&")
                            .split("boy:^&tom,girl:^&tina,cat:^&kitty,dog:^&tommy");
            ImmutableMap<String, String> expected =
                    ImmutableMap.of("boy", "tom", "girl", "tina", "cat", "kitty", "dog", "tommy");
    
            assertThat(m).isEqualTo(expected);
            assertThat(m.entrySet()).containsExactlyElementsIn(expected.entrySet()).inOrder();
        }
    
        public void testMapSplitter_emptySeparator() {
            try {
                COMMA_SPLITTER.withKeyValueSeparator("");
                fail();
            } catch (IllegalArgumentException expected) {
            }
        }
    
        public void testMapSplitter_malformedEntry() {
            try {
                COMMA_SPLITTER.withKeyValueSeparator("=").split("a=1,b,c=2");
                fail();
            } catch (IllegalArgumentException expected) {
            }
        }
    
        /**
         * Testing the behavior in https://github.com/google/guava/issues/1900 - this behavior may want to
         * be changed?
         */
        public void testMapSplitter_extraValueDelimiter() {
            try {
                COMMA_SPLITTER.withKeyValueSeparator("=").split("a=1,c=2=");
                fail();
            } catch (IllegalArgumentException expected) {
            }
        }
    
        public void testMapSplitter_orderedResults() {
            Map<String, String> m =
                COMMA_SPLITTER.withKeyValueSeparator(":").split("boy:tom,girl:tina,cat:kitty,dog:tommy");
    
            assertThat(m.keySet()).containsExactly("boy", "girl", "cat", "dog").inOrder();
            assertThat(m)
                .isEqualTo(ImmutableMap.of("boy", "tom", "girl", "tina", "cat", "kitty", "dog", "tommy"));
    
            // try in a different order
            m = COMMA_SPLITTER.withKeyValueSeparator(":").split("girl:tina,boy:tom,dog:tommy,cat:kitty");
    
            assertThat(m.keySet()).containsExactly("girl", "boy", "dog", "cat").inOrder();
            assertThat(m)
            	.isEqualTo(ImmutableMap.of("boy", "tom", "girl", "tina", "cat", "kitty", "dog", "tommy"));
        }
    
        public void testMapSplitter_duplicateKeys() {
            try {
                COMMA_SPLITTER.withKeyValueSeparator(":").split("a:1,b:2,a:3");
                fail();
            } catch (IllegalArgumentException expected) {
            }
        }
    
        public void testMapSplitter_varyingTrimLevels() {
            MapSplitter splitter = COMMA_SPLITTER.trimResults().withKeyValueSeparator(Splitter.on("->"));
            Map<String, String> split = splitter.split(" x -> y, z-> a ");
            assertThat(split).containsEntry("x ", " y");
            assertThat(split).containsEntry("z", " a");
        }
    }

7.Strings字符串处理

Strings类中存在数量有限的通用String字符串工具。

StringCharSequence实例有关的静态工具方法。

7.1静态方法

方法 描述
StringnullToEmpty(@NullableStringstring) 如果给定的字符串为非null,则返回该字符串;否则为空字符串。
StringemptyToNull(@NullableStringstring) 如果给定的字符串非空,则返回该字符串;否则为null。
booleanisNullOrEmpty(@NullableStringstring) 如果给定的字符串为null或为空字符串,则返回true。考虑使用nullToEmpty规范化字符串引用。如果这样做,则可以使用String.isEmpty()代替此方法,并且也不需要特殊的null安全形式的方法,例如String.toUpperCase。或者,如果想"以另一种方式"进行标准化,则将空字符串转换为null,则可以使用emptyToNull。
StringpadStart(Stringstring,intminLength,charpadChar) 返回一个长度至少为minLength的字符串,该字符串由string组成, 前缀 为达到该长度所需的padChar副本。例如,padStart("7",3,'0')returns"007";padStart("2010",3,'0')returns"2010"请参阅java.util.Formatter,以获取更多的格式化功能。
StringpadEnd(Stringstring,intminLength,charpadChar) 返回长度至少为minLength的字符串,该字符串由string组成, 附加 了达到该长度所需的padChar副本。例如,padEnd("4.",5,'0')returns"4.000";padEnd("2010",3,'!')returns"2010"请参阅java.util.Formatter,以获取更多的格式化功能。
Stringrepeat(Stringstring,intcount) 返回由输入字符串的特定数量的级联副本组成的字符串。例如,repeat("hey",3)返回字符串"heyheyhey"。返回一个包含string的字符串,该字符串重复了count次(如果count为零,则返回空字符串)。如果count是负数则抛出IllegalArgumentException。
StringcommonPrefix(CharSequencea,CharSequenceb) 返回最长的字符串前缀,以使a.toString().startsWith(prefix)&&b.toString().startsWith(prefix),注意不要拆分代理对。如果a和b没有共同的前缀,则返回空字符串。
StringcommonSuffix(CharSequencea,CharSequenceb) 返回最长的字符串后缀,以使a.toString().endsWith(suffix)&&b.toString().endsWith(suffix),注意不要拆分代理对。如果a和b没有共同的后缀,则返回空字符串。
StringlenientFormat(<br/>@NullableStringtemplate,@NullableObject@Nullable...args) 返回给定的template字符串,其中每次出现的"%s"被args中的相应参数值替换;或者,如果占位符和参数计数不匹配,则返回该字符串的尽力而为形式。在正常情况下不会抛出异常。 注意: 对于大多数字符串格式需求,请使用String.format,java.io.PrintWriter.format和相关方法。这些支持全部格式说明符,并提醒你通过抛出java.util.IllegalFormatException来解决使用错误。在某些情况下,例如输出调试信息或构造用于另一个未经检查的异常的消息,字符串格式化期间的异常除了取代你试图提供的真实信息外,几乎没有什么用处。这些就是这种方法适用的情况;相反,它会生成带有所有提供的参数值的尽力而为字符串。此方法在String.format不可用的GWT等环境中也很有用。例如,出于上述两个原因,Preconditions类的方法实现使用此格式化程序。 警告: 仅识别精确的两个字符的占位符序列"%s"。

7.2使用示例

    import com.google.common.annotations.GwtIncompatible;
    import com.google.common.base.Strings;
    import com.google.common.testing.NullPointerTester;
    import junit.framework.TestCase;
    
    import static com.google.common.truth.Truth.assertThat;
    
    public class StringsTest extends TestCase {
    
        public void testNullToEmpty() {
            assertEquals("", Strings.nullToEmpty(null));
            assertEquals("", Strings.nullToEmpty(""));
            assertEquals("a", Strings.nullToEmpty("a"));
        }
    
        public void testEmptyToNull() {
            assertNull(Strings.emptyToNull(null));
            assertNull(Strings.emptyToNull(""));
            assertEquals("a", Strings.emptyToNull("a"));
        }
    
        public void testIsNullOrEmpty() {
            assertTrue(Strings.isNullOrEmpty(null));
            assertTrue(Strings.isNullOrEmpty(""));
            assertFalse(Strings.isNullOrEmpty("a"));
        }
    
        public void testPadStart_noPadding() {
            assertSame("", Strings.padStart("", 0, '-'));
            assertSame("x", Strings.padStart("x", 0, '-'));
            assertSame("x", Strings.padStart("x", 1, '-'));
            assertSame("xx", Strings.padStart("xx", 0, '-'));
            assertSame("xx", Strings.padStart("xx", 2, '-'));
        }
    
        public void testPadStart_somePadding() {
            assertEquals("-", Strings.padStart("", 1, '-'));
            assertEquals("--", Strings.padStart("", 2, '-'));
            assertEquals("-x", Strings.padStart("x", 2, '-'));
            assertEquals("--x", Strings.padStart("x", 3, '-'));
            assertEquals("-xx", Strings.padStart("xx", 3, '-'));
        }
    
        public void testPadStart_negativeMinLength() {
            assertSame("x", Strings.padStart("x", -1, '-'));
        }
    
        public void testPadStart_null() {
            try {
                Strings.padStart(null, 5, '0');
                fail();
            } catch (NullPointerException expected) {
            }
        }
    
        public void testPadEnd_noPadding() {
            assertSame("", Strings.padEnd("", 0, '-'));
            assertSame("x", Strings.padEnd("x", 0, '-'));
            assertSame("x", Strings.padEnd("x", 1, '-'));
            assertSame("xx", Strings.padEnd("xx", 0, '-'));
            assertSame("xx", Strings.padEnd("xx", 2, '-'));
        }
    
        public void testPadEnd_somePadding() {
            assertEquals("-", Strings.padEnd("", 1, '-'));
            assertEquals("--", Strings.padEnd("", 2, '-'));
            assertEquals("x-", Strings.padEnd("x", 2, '-'));
            assertEquals("x--", Strings.padEnd("x", 3, '-'));
            assertEquals("xx-", Strings.padEnd("xx", 3, '-'));
        }
    
        public void testPadEnd_negativeMinLength() {
            assertSame("x", Strings.padEnd("x", -1, '-'));
        }
    
        public void testPadEnd_null() {
            try {
                Strings.padEnd(null, 5, '0');
                fail();
            } catch (NullPointerException expected) {
            }
        }
    
        public void testRepeat() {
            String input = "20";
            assertEquals("", Strings.repeat(input, 0));
            assertEquals("20", Strings.repeat(input, 1));
            assertEquals("2020", Strings.repeat(input, 2));
            assertEquals("202020", Strings.repeat(input, 3));
    
            assertEquals("", Strings.repeat("", 4));
    
            for (int i = 0; i < 100; ++i) {
                assertEquals(2 * i, Strings.repeat(input, i).length());
            }
    
            try {
                Strings.repeat("x", -1);
                fail();
            } catch (IllegalArgumentException expected) {
            }
            try {
                // Massive string
                Strings.repeat("12345678", (1 << 30) + 3);
                fail();
            } catch (ArrayIndexOutOfBoundsException expected) {
            }
        }
    
        public void testRepeat_null() {
            try {
                Strings.repeat(null, 5);
                fail();
            } catch (NullPointerException expected) {
            }
        }
    
        public void testCommonPrefix() {
            assertEquals("", Strings.commonPrefix("", ""));
            assertEquals("", Strings.commonPrefix("abc", ""));
            assertEquals("", Strings.commonPrefix("", "abc"));
            assertEquals("", Strings.commonPrefix("abcde", "xyz"));
            assertEquals("", Strings.commonPrefix("xyz", "abcde"));
            assertEquals("", Strings.commonPrefix("xyz", "abcxyz"));
            assertEquals("a", Strings.commonPrefix("abc", "aaaaa"));
            assertEquals("aa", Strings.commonPrefix("aa", "aaaaa"));
            assertEquals("abc", Strings.commonPrefix(new StringBuffer("abcdef"), "abcxyz"));
    
            // Identical valid surrogate pairs.
            assertEquals(
                    "abc\uD8AB\uDCAB", Strings.commonPrefix("abc\uD8AB\uDCABdef", "abc\uD8AB\uDCABxyz"));
            // Differing valid surrogate pairs.
            assertEquals("abc", Strings.commonPrefix("abc\uD8AB\uDCABdef", "abc\uD8AB\uDCACxyz"));
            // One invalid pair.
            assertEquals("abc", Strings.commonPrefix("abc\uD8AB\uDCABdef", "abc\uD8AB\uD8ABxyz"));
            // Two identical invalid pairs.
            assertEquals(
                    "abc\uD8AB\uD8AC", Strings.commonPrefix("abc\uD8AB\uD8ACdef", "abc\uD8AB\uD8ACxyz"));
            // Two differing invalid pairs.
            assertEquals("abc\uD8AB", Strings.commonPrefix("abc\uD8AB\uD8ABdef", "abc\uD8AB\uD8ACxyz"));
            // One orphan high surrogate.
            assertEquals("", Strings.commonPrefix("\uD8AB\uDCAB", "\uD8AB"));
            // Two orphan high surrogates.
            assertEquals("\uD8AB", Strings.commonPrefix("\uD8AB", "\uD8AB"));
        }
    
        public void testCommonSuffix() {
            assertEquals("", Strings.commonSuffix("", ""));
            assertEquals("", Strings.commonSuffix("abc", ""));
            assertEquals("", Strings.commonSuffix("", "abc"));
            assertEquals("", Strings.commonSuffix("abcde", "xyz"));
            assertEquals("", Strings.commonSuffix("xyz", "abcde"));
            assertEquals("", Strings.commonSuffix("xyz", "xyzabc"));
            assertEquals("c", Strings.commonSuffix("abc", "ccccc"));
            assertEquals("aa", Strings.commonSuffix("aa", "aaaaa"));
            assertEquals("abc", Strings.commonSuffix(new StringBuffer("xyzabc"), "xxxabc"));
    
            // Identical valid surrogate pairs.
            assertEquals(
                    "\uD8AB\uDCABdef", Strings.commonSuffix("abc\uD8AB\uDCABdef", "xyz\uD8AB\uDCABdef"));
            // Differing valid surrogate pairs.
            assertEquals("def", Strings.commonSuffix("abc\uD8AB\uDCABdef", "abc\uD8AC\uDCABdef"));
            // One invalid pair.
            assertEquals("def", Strings.commonSuffix("abc\uD8AB\uDCABdef", "xyz\uDCAB\uDCABdef"));
            // Two identical invalid pairs.
            assertEquals(
                    "\uD8AB\uD8ABdef", Strings.commonSuffix("abc\uD8AB\uD8ABdef", "xyz\uD8AB\uD8ABdef"));
            // Two differing invalid pairs.
            assertEquals("\uDCABdef", Strings.commonSuffix("abc\uDCAB\uDCABdef", "abc\uDCAC\uDCABdef"));
            // One orphan low surrogate.
            assertEquals("", Strings.commonSuffix("x\uD8AB\uDCAB", "\uDCAB"));
            // Two orphan low surrogates.
            assertEquals("\uDCAB", Strings.commonSuffix("\uDCAB", "\uDCAB"));
        }
    
        public void testLenientFormat() {
            assertEquals("%s", Strings.lenientFormat("%s"));
            assertEquals("5", Strings.lenientFormat("%s", 5));
            assertEquals("foo [5]", Strings.lenientFormat("foo", 5));
            assertEquals("foo [5, 6, 7]", Strings.lenientFormat("foo", 5, 6, 7));
            assertEquals("%s 1 2", Strings.lenientFormat("%s %s %s", "%s", 1, 2));
            assertEquals(" [5, 6]", Strings.lenientFormat("", 5, 6));
            assertEquals("123", Strings.lenientFormat("%s%s%s", 1, 2, 3));
            assertEquals("1%s%s", Strings.lenientFormat("%s%s%s", 1));
            assertEquals("5 + 6 = 11", Strings.lenientFormat("%s + 6 = 11", 5));
            assertEquals("5 + 6 = 11", Strings.lenientFormat("5 + %s = 11", 6));
            assertEquals("5 + 6 = 11", Strings.lenientFormat("5 + 6 = %s", 11));
            assertEquals("5 + 6 = 11", Strings.lenientFormat("%s + %s = %s", 5, 6, 11));
            assertEquals("null [null, null]", Strings.lenientFormat("%s", null, null, null));
            assertEquals("null [5, 6]", Strings.lenientFormat(null, 5, 6));
            assertEquals("null", Strings.lenientFormat("%s", (Object) null));
            assertEquals("(Object[])null", Strings.lenientFormat("%s", (Object[]) null));
        }
    
        @GwtIncompatible // GWT reflection includes less data
        public void testLenientFormat_badArgumentToString() {
            assertThat(Strings.lenientFormat("boiler %s plate", new ThrowsOnToString()))
                    .matches(
                        "boiler <com\\.google\\.common\\.base\\.StringsTest\\$ThrowsOnToString@[0-9a-f]+ "
                            + "threw java\\.lang\\.UnsupportedOperationException> plate");
        }
    
        public void testLenientFormat_badArgumentToString_gwtFriendly() {
            assertThat(Strings.lenientFormat("boiler %s plate", new ThrowsOnToString()))
                    .matches("boiler <.*> plate");
        }
    
        private static class ThrowsOnToString {
            @Override
            public String toString() {
                throw new UnsupportedOperationException();
            }
        }
    
        @GwtIncompatible // NullPointerTester
        public void testNullPointers() {
            NullPointerTester tester = new NullPointerTester();
            tester.testAllPublicStaticMethods(Strings.class);
        }
    }

本文参考:
CharMatcher
Charsets
CaseFormat
Joiner
Splitter
Strings
guava-tests-base

阅读全文