Разбить строку, игнорируя разделы в кавычках

Учитывая строку как это:

a, «строка, с», различные, «значения и некоторые», в кавычках

Что является хорошим алгоритмом для разделения этого на основе запятых, игнорируя запятые внутри цитируемых разделов?

Вывод должен быть массивом:

["a", "строка, с", "различными", "значениями и некоторыми", "в кавычках"]

8.08.2008 18:04:03
Что, если в исходной строке появится нечетное количество кавычек?
Brock D 8.08.2008 18:12:06
Это подразумевало бы неверно заключенную в кавычки входную строку, поэтому выбрасывание исключения было бы вариантом.
J c 4.11.2008 09:46:57
как бы вы поместили цитату в кавычки?
anthony 30.01.2019 01:31:04
13 ОТВЕТОВ
РЕШЕНИЕ

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

  1. Выполните предварительный анализ и замените запятые в строке другим управляющим символом, затем разделите их, а затем выполните повторный анализ массива, чтобы заменить ранее использовавшийся управляющий символ на запятые.

  2. Или же разделите их на запятые, а затем проанализируйте полученный массив в другой массив, проверяя ведущие кавычки на каждой записи массива и объединяя записи, пока я не достигну завершающей кавычки.

Это, однако, хаки, и если это чисто «умственное» упражнение, то я подозреваю, что они окажутся бесполезными. Если это проблема реального мира, то было бы полезно знать язык, чтобы мы могли дать какой-то конкретный совет.

1
20.12.2016 09:31:59
Я ищу алгоритм для аналогичной проблемы, где я должен обрабатывать огромные текстовые файлы (в ГБ). Эти текстовые файлы содержат уточненные данные, т.е. разделитель поля / записи является частью данных, заключенных в одинарные / двойные кавычки. Я ищу алгоритм, который может помочь мне обрабатывать эти файлы параллельно (несколькими потоками). Язык, который мы используем - это Java. Дайте мне знать, если у вас есть какие-либо предложения
Andy Dufresne 11.06.2013 05:56:14

Конечно, лучше использовать CSV-парсер, но просто для удовольствия вы можете:

Loop on the string letter by letter.
    If current_letter == quote : 
        toggle inside_quote variable.
    Else if (current_letter ==comma and not inside_quote) : 
        push current_word into array and clear current_word.
    Else 
        append the current_letter to current_word
When the loop is done push the current_word into array 
2
8.04.2009 12:35:54

Автор здесь добавил код C #, который обрабатывает сценарий, с которым у вас возникли проблемы:

Импорт файлов CSV в .Net

Не должно быть слишком сложно для перевода.

1
23.05.2017 12:17:19

Я использую это для разбора строк, не уверен, поможет ли это здесь; но с некоторыми незначительными изменениями возможно?

function getstringbetween($string, $start, $end){
    $string = " ".$string;
    $ini = strpos($string,$start);
    if ($ini == 0) return "";
    $ini += strlen($start);   
    $len = strpos($string,$end,$ini) - $ini;
    return substr($string,$ini,$len);
}

$fullstring = "this is my [tag]dog[/tag]";
$parsed = getstringbetween($fullstring, "[tag]", "[/tag]");

echo $parsed; // (result = dog) 

/ тр

0
8.08.2008 18:36:07

Вот простой алгоритм:

  1. Определите, начинается ли строка с '"'символа
  2. Разбейте строку на массив, разделенный '"'символом.
  3. Пометьте кавычки в качестве заполнителя #COMMA#
    • Если ввод начинается с a '"', отметьте те элементы в массиве, где индекс% 2 == 0
    • В противном случае отметьте те элементы в массиве, где индекс% 2 == 1.
  4. Объединить элементы в массиве, чтобы сформировать измененную входную строку.
  5. Разбейте строку на массив, разделенный ','символом.
  6. Замените все экземпляры в массиве #COMMA#заполнителей ','символом.
  7. Массив - это ваш вывод.

Вот реализация Python:
(исправлено для обработки '' a, b ", c," d, e, f, h "," i, j, k "')

def parse_input(input):

    quote_mod = int(not input.startswith('"'))

    input = input.split('"')
    for item in input:
        if item == '':
            input.remove(item)
    for i in range(len(input)):
        if i % 2 == quoted_mod:
            input[i] = input[i].replace(",", "#COMMA#")

    input = "".join(input).split(",")
    for item in input:
        if item == '':
            input.remove(item)
    for i in range(len(input)):
        input[i] = input[i].replace("#COMMA#", ",")
    return input

# parse_input('a,"string, with",various,"values, and some",quoted')
#  -> ['a,string', ' with,various,values', ' and some,quoted']
# parse_input('"a,b",c,"d,e,f,h","i,j,k"')
#  -> ['a,b', 'c', 'd,e,f,h', 'i,j,k']
0
8.08.2008 19:36:36

Это стандартный разбор в CSV-стиле. Многие люди пытаются сделать это с помощью регулярных выражений. Вы можете получить около 90% с помощью регулярных выражений, но вам действительно нужен настоящий анализатор CSV, чтобы сделать это правильно. Несколько месяцев назад я нашел быстрый, отличный синтаксический анализатор C # CSV на CodeProject, который я очень рекомендую!

0
8.08.2008 18:52:50
Конечно, есть и в .NET Framework. Даже если он находится в Microsoft.VisualBasic, вы все равно можете использовать его из C #. msdn.microsoft.com/en-us/library/…
MarkJ 2.04.2009 09:17:19
Спасибо! Как C # парень, я всегда забываю, что есть куча полезных библиотек VB, которые я могу использовать. Честно говоря, я думаю, что они плохо названы, так как они на самом деле не VB. Они просто .NET.
Simon Gillbee 14.04.2009 17:39:07

Вот один из псевдокодов (он же Python) за один проход :-P

def parsecsv(instr):
    i = 0
    j = 0

    outstrs = []

    # i is fixed until a match occurs, then it advances
    # up to j. j inches forward each time through:

    while i < len(instr):

        if j < len(instr) and instr[j] == '"':
            # skip the opening quote...
            j += 1
            # then iterate until we find a closing quote.
            while instr[j] != '"':
                j += 1
                if j == len(instr):
                    raise Exception("Unmatched double quote at end of input.")

        if j == len(instr) or instr[j] == ',':
            s = instr[i:j]  # get the substring we've found
            s = s.strip()    # remove extra whitespace

            # remove surrounding quotes if they're there
            if len(s) > 2 and s[0] == '"' and s[-1] == '"':
                s = s[1:-1]

            # add it to the result
            outstrs.append(s)

            # skip over the comma, move i up (to where
            # j will be at the end of the iteration)
            i = j+1

        j = j+1

    return outstrs

def testcase(instr, expected):
    outstr = parsecsv(instr)
    print outstr
    assert expected == outstr

# Doesn't handle things like '1, 2, "a, b, c" d, 2' or
# escaped quotes, but those can be added pretty easily.

testcase('a, b, "1, 2, 3", c', ['a', 'b', '1, 2, 3', 'c'])
testcase('a,b,"1, 2, 3" , c', ['a', 'b', '1, 2, 3', 'c'])

# odd number of quotes gives a "unmatched quote" exception
#testcase('a,b,"1, 2, 3" , "c', ['a', 'b', '1, 2, 3', 'c'])
0
8.08.2008 19:15:34

Похоже, у вас есть хорошие ответы здесь.

Для тех из вас, кто хочет разобраться с собственным анализом CSV-файлов, прислушайтесь к советам экспертов и не катите свой собственный анализатор CSV .

Ваша первая мысль: «Мне нужно обрабатывать запятые внутри кавычек».

Ваша следующая мысль будет: «О, дерьмо, мне нужно обрабатывать кавычки внутри кавычек. Кавычки, к которым вы обращаетесь. Двойные кавычки. Одинарные кавычки ...»

Это дорога к безумию. Не пиши свое. Найдите библиотеку с обширным охватом модульных тестов, которая поразит все сложные части и прошла через ад. Для .NET используйте бесплатную библиотеку FileHelpers .

21
8.08.2008 19:36:34
отличная ссылка на secretgeek - очень забавно. но это только ответ на вопрос для тех, кто использует .NET, к сожалению.
Magnus Smith 24.03.2009 16:18:54
Правда; хотя этот совет применим ко всем разработчикам: не используйте свой собственный анализатор CSV. В Ruby есть один встроенный, и есть библиотеки для Python, C ++, большинство любого широко используемого языка.
Judah Gabriel Himango 25.03.2009 14:21:16
И хотя SecretGeek, похоже, этого не знает, есть еще один, встроенный в VB.NET. msdn.microsoft.com/en-us/library/…
MarkJ 2.04.2009 09:16:21

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

arr = [i.replace("|", ",") for i in re.sub('"([^"]*)\,([^"]*)"',"\g<1>|\g<2>", str_to_test).split(",")]

Возвращает ['a', 'string, with', 'Various', 'values ​​и some', 'quoted']

Он работает, сначала заменяя внутри кавычки ',' на другой разделитель (|), разделяя строку на ',' и заменяя | разделитель снова.

0
8.08.2008 19:59:01
Откуда ты знаешь, что их нет? в исходной строке? Как насчет экранирования кавычек внутри строк в кавычках?
MarkJ 2.04.2009 09:18:22

Python:

import csv
reader = csv.reader(open("some.csv"))
for row in reader:
    print row
6
1.10.2009 21:54:30
Я считаю это лучшим ответом. Это именно то, что мне нужно!
Alex. S. 9.01.2009 00:12:01

Что, если в исходной строке появится нечетное количество кавычек?

Это выглядит странно, как разбор CSV, который имеет некоторые особенности для обработки полей в кавычках. Поле экранируется только в том случае, если оно разделено двойными кавычками, поэтому:

field1, «field2, field3», field4, «field5, field6» field7

становится

field1

поле2, поле3

Field4

«Field5

field6 "field7

Обратите внимание: если оно не начинается и не заканчивается кавычкой, тогда это не поле в кавычках, а двойные кавычки просто обрабатываются как двойные кавычки.

Внезапно мой код, с которым кто-то связался, на самом деле не обрабатывает это правильно, если я правильно помню.

1
8.08.2008 22:21:53

Вот простая реализация Python, основанная на псевдокоде Пата:

def splitIgnoringSingleQuote(string, split_char, remove_quotes=False):
    string_split = []
    current_word = ""
    inside_quote = False
    for letter in string:
      if letter == "'":
        if not remove_quotes:
           current_word += letter
        if inside_quote:
          inside_quote = False
        else:
          inside_quote = True
      elif letter == split_char and not inside_quote:
        string_split.append(current_word)
        current_word = ""
      else:
        current_word += letter
    string_split.append(current_word)
    return string_split
1
5.10.2010 05:43:24

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

def find_character_indices(s, ch):
    return [i for i, ltr in enumerate(s) if ltr == ch]


def split_text_preserving_quotes(content, include_quotes=False):
    quote_indices = find_character_indices(content, '"')

    output = content[:quote_indices[0]].split()

    for i in range(1, len(quote_indices)):
        if i % 2 == 1: # end of quoted sequence
            start = quote_indices[i - 1]
            end = quote_indices[i] + 1
            output.extend([content[start:end]])

        else:
            start = quote_indices[i - 1] + 1
            end = quote_indices[i]
            split_section = content[start:end].split()
            output.extend(split_section)

        output += content[quote_indices[-1] + 1:].split()                                                                 

    return output
0
15.10.2018 02:37:26