Разделить строку, игнорируя цитируемые разделы

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

a,"string, with",various,"values, and some",quoted

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

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

[ "a", "string, with", "various", "values, and some", "quoted" ]

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

Brock D 08.08.2008 22:12

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

J c 04.11.2008 12:46

как бы вы поместили цитату в кавычки?

anthony 30.01.2019 04:31
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
22
3
9 863
13
Перейти к ответу Данный вопрос помечен как решенный

Ответы 13

Конечно, лучше использовать парсер 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 

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

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

Перевод не должен быть слишком сложным.

Ответ принят как подходящий

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

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

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

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

Я ищу алгоритм для аналогичной проблемы, когда мне нужно обрабатывать огромные текстовые файлы (в ГБ). Эти текстовые файлы содержат квалифицированные данные, то есть разделитель полей / записей является частью данных, если они заключены в одинарные / двойные кавычки. Я ищу алгоритм, который поможет мне обрабатывать эти файлы параллельно (в нескольких потоках). Мы используем язык Java. Дайте мне знать, если у вас есть предложения

Andy Dufresne 11.06.2013 09:56

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

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) 

/ мп

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

  1. Определите, начинается ли строка с символа '"'
  2. Разделите строку на массив, разделенный символом '"'.
  3. Пометьте кавычки запятыми с помощью заполнителя #COMMA#
    • Если ввод начинается с '"', отметьте те элементы в массиве, где индекс% 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']

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

Конечно, есть еще один в .NET framework. Несмотря на то, что он находится в Microsoft.VisualBasic, вы все равно можете использовать его из C#. msdn.microsoft.com/en-us/library/…

MarkJ 02.04.2009 13:17

Спасибо! Как специалист по C#, я всегда забываю, что есть множество полезных библиотек VB, которые я могу использовать. Честно говоря, я думаю, что они плохо названы, поскольку на самом деле это не VB. Это просто .NET.

Simon Gillbee 14.04.2009 21:39

Вот один псевдокод (он же 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'])

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

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

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

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

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

отличная ссылка на secretgeek - очень забавно. но он, к сожалению, отвечает только на вопрос тех, кто использует .NET.

Magnus Smith 24.03.2009 19:18

Истинный; хотя совет применим ко всем разработчикам: не используйте собственный анализатор CSV. Ruby имеет один встроенный модуль, и существуют библиотеки для Python, C++, большинства широко используемых языков.

Judah Gabriel Himango 25.03.2009 17:21

И хотя SecretGeek, похоже, этого не знает, он тоже встроен в VB.NET. msdn.microsoft.com/en-us/library/…

MarkJ 02.04.2009 13:16

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

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

Возвращает ['a', 'строка, с', 'различными', 'значениями и некоторыми', 'цитируемыми']

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

Откуда вы знаете, что их нет | в исходной строке? А как насчет экранирования кавычек внутри строк в кавычках?

MarkJ 02.04.2009 13:18

Python:

import csv
reader = csv.reader(open("some.csv"))
for row in reader:
    print row

Считаю это лучшим ответом. Это именно то, что мне нужно!

Alex. S. 09.01.2009 03:12

What if an odd number of quotes appear in the original string?

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

field1, "field2, field3", field4, "field5, field6" field7

становится

field1

field2, field3

field4

"field5

field6" field7

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

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

Вот простая реализация 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

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

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

Другие вопросы по теме