Разделение строки через запятую, игнорирование запятых в кавычках, но разрешение строк с одной двойной кавычкой

Я просмотрел несколько сообщений на stackru о том, как разбить строку на разделитель запятыми, но игнорировать разбиение на запятую в кавычках (см.: Как разбить строку на массив по запятой, но игнорировать запятые в двойных кавычках?) Я пытаюсь для достижения аналогичных результатов, но необходимо также учитывать строку, содержащую одну двойную кавычку.

IE. Необходимость "test05, \"test, 05\", test\", test 05" разделить на

  • test05
  • "test, 05"
  • test"
  • test 05

Я попробовал аналогичный метод, упомянутый здесь:

Регулярное выражение для разделения строки с использованием пробела, когда оно не заключено в одинарные или двойные кавычки

Использование Matcher вместо split(), однако, что конкретные примеры он разделяет на пробелы, а не на запятые. Вместо этого я попытался настроить шаблон для учета запятых, но мне не повезло.

String str = "test05, \"test, 05\", test\", test 05";
str = str + " "; // add trailing space
int len = str.length();
Matcher m = Pattern.compile("((\"[^\"]+?\")|([^,]+?)),++").matcher(str);

for (int i = 0; i < len; i++)
{
    m.region(i, len);

    if (m.lookingAt())
    {
        String s = m.group(1);

        if ((s.startsWith("\"") && s.endsWith("\"")))
        {
            s = s.substring(1, s.length() - 1);
        }

        System.out.println(i + ": \"" + s + "\"");
        i += (m.group(0).length() - 1);
    }
}

5 ответов

У меня были похожие проблемы с этим, и я не нашел хорошего решения.net, поэтому пошел DIY.

В моем приложении я анализирую CSV, поэтому мои разделенные учетные данные ",". этот метод, я полагаю, работает только там, где у вас есть один аргумент разделения символов

Итак, я написал функцию, которая игнорирует запятые в двойных кавычках. он делает это путем преобразования входной строки в массив символов и разбора char на char

public static string[] Splitter_IgnoreQuotes(string stringToSplit)
    {   
        char[] CharsOfData = stringToSplit.ToCharArray();
        //enter your expected array size here or alloc.
        string[] dataArray = new string[37];
        int arrayIndex = 0;
        bool DoubleQuotesJustSeen = false;          
        foreach (char theChar in CharsOfData)
        {
            //did we just see double quotes, and no command? dont split then. you could make ',' a variable for your split parameters I'm working with a csv.
            if ((theChar != ',' || DoubleQuotesJustSeen) && theChar != '"')
            {
                dataArray[arrayIndex] = dataArray[arrayIndex] + theChar;
            }
            else if (theChar == '"')
            {
                if (DoubleQuotesJustSeen)
                {
                    DoubleQuotesJustSeen = false;
                }
                else
                {
                    DoubleQuotesJustSeen = true;
                }
            }
            else if (theChar == ',' && !DoubleQuotesJustSeen)
            {
                arrayIndex++;
            }
        }
        return dataArray;
    }

Эта функция, на мой вкус приложения, также игнорирует ("") при любом вводе, поскольку они не нужны и присутствуют в моем вводе.

Вы достигли точки, где регулярные выражения ломаются.

Я бы порекомендовал вам написать простой сплиттер, который обрабатывает ваши особые случаи так, как вы пожелаете. Test Driven Development отлично подходит для этого.

Однако похоже, что вы пытаетесь разобрать строки CSV. Рассматривали ли вы использование CSV-библиотеки для этого?

Если вам не нужно заниматься DIY, вы должны рассмотреть класс Apache Commons org.apache.commons.csv.CSVParser

http://commons.apache.org/sandbox/csv/apidocs/org/apache/commons/csv/CSVParser.html

Сплит против этой картины:

(?<=\"?),(?!\")|(?<!\"),(?=\")

так будет:

String[] splitArray = subjectString.split("(?<=\"?),(?!\")|(?<!\"),(?=\")");

UPD: в соответствии с недавними изменениями в логике вопросов, лучше не использовать разделение на части, сначала нужно отделить текст через запятую от текста без запятых, а затем выполнить простое разделение (",") на последнем. Просто используйте простой цикл for и проверьте, сколько кавычек вы встретили, одновременно сохраняя прочитанные символы в StringBuffer. Сначала вы сохраняете свои символы в StringBuffer, пока не встретите кавычки, затем вы помещаете свой StringBuffer в массив, содержащий строки, которых не было в кавычках. Затем вы создаете новый StringBuffer и сохраняете следующие символы, которые вы читаете, после того, как вы встретили вторую запятую, вы останавливаете и помещаете свой новый StringBuffer в массив, содержащий строки, которые были в запятых. Повторяя до конца строки. Таким образом, у вас будет 2 массива, один со строками, которые были в запятых, другие со строками, не в запятых. Затем вы должны разделить все элементы второго массива.

Попробуй это:

import java.util.regex.*;

public class Main {
  public static void main(String[] args) throws Exception {

    String text = "test05, \"test, 05\", test\", test 05";

    Pattern p = Pattern.compile(
        "(?x)          # enable comments                                      \n" +
        "(\"[^\"]*\")  # quoted data, and store in group #1                   \n" +
        "|             # OR                                                   \n" +
        "([^,]+)       # one or more chars other than ',', and store it in #2 \n" +
        "|             # OR                                                   \n" +
        "\\s*,\\s*     # a ',' optionally surrounded by space-chars           \n"
    );

    Matcher m = p.matcher(text);

    while (m.find()) {
      // get the match
      String matched = m.group().trim();

      // only print the match if it's group #1 or #2
      if(m.group(1) != null || m.group(2) != null) {
        System.out.println(matched);
      }
    }
  }
}

За test05, "test, 05", test", test 05 он производит:

test05
"тест, 05"
тестовое задание"
тест 05

и для test05, "test 05", test", test 05 он производит:

test05
"Тест 05"
тестовое задание"
тест 05
Другие вопросы по тегам