2011-04-21 45 views
4

我想創建一個可以將Java字符串作爲一個塊處理的Java Guava Splitter。舉例來說,我想下面的說法是正確的:創建一個字符串功能的Guava Splitter

@Test 
public void testSplitter() { 
    String toSplit = "a,b,\"c,d\\\"\",e"; 
    List<String> expected = ImmutableList.of("a", "b", "c,d\"","e"); 

    Splitter splitter = Splitter.onPattern(...); 
    List<String> actual = ImmutableList.copyOf(splitter.split(toSplit)); 

    assertEquals(expected, actual); 
} 

我可以寫正則表達式來發現所有的元素和不考慮「」但我不能找到將採取行動的正則表達式作爲與分離器一起使用的分離器。

如果不可能,請說出來,然後我將從findAll正則表達式構建列表。

回答

4

這看起來像你應該使用CSV庫,如opencsv。分隔值和處理像引用塊這樣的情況是他們的全部內容。

2

分以下模式:

\s*,\s*(?=((\\["\\]|[^"\\])*"(\\["\\]|[^"\\])*")*(\\["\\]|[^"\\])*$) 

這看起來(有點)與(?x)標誌友好:

(?x)   # enable comments, ignore space-literals 
\s*,\s*   # match a comma optionally surrounded by space-chars 
(?=    # start positive look ahead 
    (   # start group 1 
    (   #  start group 2 
     \\["\\] #  match an escaped quote or backslash 
     |   #  OR 
     [^"\\] #  match any char other than a quote or backslash 
    )*   #  end group 2, and repeat it zero or more times 
    "   #  match a quote 
    (   #  start group 3 
     \\["\\] #  match an escaped quote or backslash 
     |   #  OR 
     [^"\\] #  match any char other than a quote or backslash 
    )*   #  end group 3, and repeat it zero or more times 
    "   #  match a quote 
)*   # end group 1, and repeat it zero or more times 
    (   # open group 4 
    \\["\\]  #  match an escaped quote or backslash 
    |   #  OR 
    [^"\\]  #  match any char other than a quote or backslash 
)*   # end group 4, and repeat it zero or more times 
    $    # match the end-of-input 
)    # end positive look ahead 

但即使在這種註釋的版本,它仍然是一個怪物。用簡單的英語,這正則表達式可以解釋如下:(!一路字符串的結尾)

匹配任選被空字符包圍的逗號,放眼望去只有當逗號,有零或偶數的引號,而忽略逃脫的引號或反斜線。

所以,看到這個之後,你也許會同意ColinD(我做的!),使用某種一個CSV解析器的是在這種情況下要走的路。

注意,正則表達式以上將離開qoutes周圍的令牌,即,串a,b,"c,d\"",e(作爲文字:"a,b,\"c,d\\\"\",e")將如下拆分:

a 
b 
"c,d\"" 
e 
+0

我+1了您的答案,因爲它(幾乎)正是我想要使用我想要的工具,但爲了可讀性,我採取了ColinD的答案。無論如何,非常非常好的東西! – 2011-04-21 22:26:27

+1

@ogregoire,我完全同意。我主要張貼它來展示你最終會得到一個可怕的正則表達式:這樣一個野獸不應該在野外放出!:) – 2011-04-22 06:24:41

4

我有同樣的問題(除了不需要支持引號字符的轉義)。我不喜歡爲這樣簡單的事情添加另一個庫。然後我想到了,我需要一個可變的CharMatcher。與Bart Kiers的解決方案一樣,它保持引用字符。

public static Splitter quotableComma() { 
    return on(new CharMatcher() { 
     private boolean inQuotes = false; 

     @Override 
     public boolean matches(char c) { 
      if ('"' == c) { 
       inQuotes = !inQuotes; 
      } 
      if (inQuotes) { 
       return false; 
      } 
      return (',' == c); 
     } 
    }); 
} 

@Test 
public void testQuotableComma() throws Exception { 
    String toSplit = "a,b,\"c,d\",e"; 
    List<String> expected = ImmutableList.of("a", "b", "\"c,d\"", "e"); 
    Splitter splitter = Splitters.quotableComma(); 
    List<String> actual = ImmutableList.copyOf(splitter.split(toSplit)); 
    assertEquals(expected, actual); 
} 
+0

我有同樣的問題,但我甚至沒有想到一個新的CharMatcher。謝謝! – 2013-12-18 00:21:45

0

改進@ Rage-Steel的答案有點。

final static CharMatcher notQuoted = new CharMatcher() { 
    private boolean inQuotes = false; 

    @Override 
    public boolean matches(char c) { 
     if ('"' == c) { 
     inQuotes = !inQuotes; 
    } 
    return !inQuotes; 
}; 

final static Splitter SPLITTER = Splitter.on(notQuoted.and(CharMatcher.anyOf(" ,;|"))).trimResults().omitEmptyStrings(); 

然後,

public static void main(String[] args) { 
    final String toSplit = "a=b c=d,kuku=\"e=f|g=h something=other\""; 

    List<String> sputnik = SPLITTER.splitToList(toSplit); 
    for (String s : sputnik) 
     System.out.println(s); 
} 

注重線程安全(或簡化 - 沒有任何)