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

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

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

Ответ 1

Вы достигли точки разрыва регулярных выражений.

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

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

Ответ 3

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

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

Итак, я написал функцию, которая игнорирует запятые в двойных кавычках. он делает это путем преобразования входной строки в массив символов и разбора 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;
    }

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

Ответ 4

Разделите этот шаблон:

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

поэтому он будет:

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

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

Ответ 5

Попробуйте следующее:

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 group #1 or #2
      if(m.group(1) != null || m.group(2) != null) {
        System.out.println(matched);
      }
    }
  }
}

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

test05
"test, 05"
test"
test 05

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

test05
"test 05"
test"
test 05