2011-04-21 3 views
4

Java 문자열을 하나의 블록으로 처리 할 수있는 Java 용 구아바 분배기를 만들고 싶습니다. 예를 들어, 나는 사실로 다음과 같은 주장을하고 싶습니다 :문자열 지원 구아바 분배기 만들기

@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

이것은 opencsv과 같은 CSV 라이브러리를 사용해야하는 것처럼 보입니다. 값을 분리하고 따옴표 붙은 블록과 같은 사례를 처리하는 것이 모두에 관한 것입니다.

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 

그러나 심지어이 주석 처리 된 버전, 그것을 아직도 괴물이다. 다음과 같이 일반 영어,이 정규식은 설명 할 수있다 : (! 문자열의 끝까지 모든 방법을)

일치 선택적으로 공간 문자로 둘러싸여 쉼표, 그 쉼표의 계속 찾고에만 이스케이프 된 따옴표 또는 이스케이프 된 백 슬래시를 무시하는 동안 0 또는 짝수 개의 따옴표가 있습니다.

그래서,이 문제를보고 한 후, 당신은 (내가!)를 CSV 파서의 일종을 사용하는 것이이 경우에 갈 수있는 방법이라고 ColinD에 동의 할 수도 있습니다. 정규식 위 (리터럴로 : "a,b,\"c,d\\\"\",e"), 즉 토큰, 주위에 문자열 a,b,"c,d\"",e을 qoutes를 떠날 것이라고

참고 다음과 같이 분할됩니다 :

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

나는 그것이 않기 때문에 답변을 +1했습니다 (거의) 정확히 내가 원하는 도구를 사용하여 원하는,하지만 가독성을 위해서, 나는 ColinD의 대답을했습니다. 어쨌든, 아주 좋은 물건! –

+1

@ogregoire, 전적으로 동의합니다. 나는 주로 괴물 같은 정규 표현식을 보여주기 위해 게시했습니다. 그러한 짐승은 야생에서 나오면 안됩니다!:) –

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에 대해서는 생각조차하지 않았다. 감사! –

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); 
} 

에주의 안전을 스레드 (또는, 단순화하기 - 어떤이없는)