Создайте совместимый со строкой Guava Splitter - PullRequest
4 голосов
/ 21 апреля 2011

Я хотел бы создать разделитель Guava для 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);
}

Я могу написать регулярное выражение, чтобы найти все элементы, и не учитывать ',', но я не могу найти регулярное выражение, которое будет действовать как разделитель для использования со Splitter.

Если это невозможно, просто скажите, тогда я построю список из регулярного выражения findAll.

Ответы [ 5 ]

4 голосов
/ 13 ноября 2013

У меня та же проблема (за исключением того, что нет необходимости поддерживать экранирование символа кавычки). Я не люблю включать другую библиотеку для такой простой вещи. И тогда я пришел к мысли, что мне нужен изменчивый 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);
}
4 голосов
/ 22 апреля 2011

Это запрос функции Guava: http://code.google.com/p/guava-libraries/issues/detail?id=412

4 голосов
/ 21 апреля 2011

Это похоже на то, что вы должны использовать библиотеку CSV, например, opencsv для.Разделение значений и обработка случаев, таких как блоки в кавычках, - вот о чем они.

2 голосов
/ 21 апреля 2011

Вы можете разделить по следующему шаблону:

\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.

Обратите внимание, что приведенное выше регулярное выражение оставит квоты вокруг токенов, то есть строка a,b,"c,d\"",e (как литерал: "a,b,\"c,d\\\"\",e") будет разбита следующим образом:

a
b
"c,d\""
e
0 голосов
/ 24 августа 2015

Немного улучшается ответ @ 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);
}

Обратите внимание на безопасность потоков (или, чтобы упростить - их нет)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...