Google Guava與字符串操作相關的類

使用示例時須導入以下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 &gt; 5", is('>').replaceFrom("12 > 5", "&gt;"));
    }

    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靜態方法

方法 描述
String nullToEmpty(@Nullable String string) 如果給定的字符串爲非null,則返回該字符串;否則爲空字符串。
String emptyToNull(@Nullable String string) 如果給定的字符串非空,則返回該字符串;否則爲null
boolean isNullOrEmpty(@Nullable String string) 如果給定的字符串爲null或爲空字符串,則返回true。考慮使用nullToEmpty規範化字符串引用。
如果這樣做,則可以使用String.isEmpty()代替此方法,並且也不需要特殊的null安全形式的方法,例如String.toUpperCase。或者,如果想"以另一種方式"進行標準化,則將空字符串轉換爲null,則可以使用emptyToNull
String padStart(String string, int minLength, char padChar) 返回一個長度至少爲minLength的字符串,該字符串由string組成,前綴爲達到該長度所需的padChar副本。例如,
padStart("7", 3, '0') returns "007"; padStart("2010", 3, '0') returns "2010"
請參閱java.util.Formatter,以獲取更多的格式化功能。
String padEnd(String string, int minLength, char padChar) 返回長度至少爲minLength的字符串,該字符串由string組成,附加了達到該長度所需的padChar副本。例如,
padEnd("4.", 5, '0') returns "4.000"; padEnd("2010", 3, '!') returns "2010"
請參閱java.util.Formatter,以獲取更多的格式化功能。
String repeat(String string, int count) 返回由輸入字符串的特定數量的級聯副本組成的字符串。例如,repeat("hey", 3)返回字符串"heyheyhey"。返回一個包含string的字符串,該字符串重複了count次(如果count爲零,則返回空字符串)。如果count是負數則拋出IllegalArgumentException
String commonPrefix(CharSequence a, CharSequence b) 返回最長的字符串前綴,以使a.toString().startsWith(prefix) && b.toString().startsWith(prefix),注意不要拆分代理對。如果ab沒有共同的前綴,則返回空字符串。
String commonSuffix(CharSequence a, CharSequence b) 返回最長的字符串後綴,以使a.toString().endsWith(suffix) && b.toString().endsWith(suffix),注意不要拆分代理對。如果ab沒有共同的後綴,則返回空字符串。
String lenientFormat(<br/> @Nullable String template, @Nullable Object @Nullable ... args) 返回給定的template字符串,其中每次出現的"%s"args中的相應參數值替換;或者,如果佔位符和參數計數不匹配,則返回該字符串的盡力而爲形式。在正常情況下不會拋出異常。
注意: 對於大多數字符串格式需求,請使用String.formatjava.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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章