Анализ CSV файла, заключенного в кавычки в С#

Я видел множество образцов при анализе CSV файла. но это один из видов раздражающего файла...

так как вы разбираете этот тип CSV

"1", 1/2/2010, "Образец (" adasdad ") asdada", "Я вскакивал в дверь" Stinky ", так что я буду чертовски", "AK"

Ответ 1

Лучший ответ в большинстве случаев, вероятно, @Jim Mischel's. TextFieldParser кажется, именно то, что вы хотите для большинства обычных случаев - хотя он странным образом живет в пространстве имен Microsoft.VisualBasic ! Но этот случай не является обычным.

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

Поэтому я переписал для этого случая как расширение строки. Я думаю, что это близко.

Заметьте, что "I was pooping in the door "Stinky", so I'll be damn", это особенно неприятный случай. Без *** STINKY CONDITION ***, приведенного ниже, вы получите, что I was pooping in the door "Stinky как одно значение, so I'll be damn" как другое.

Единственный способ добиться большего успеха, чем в любом анонимном странном случае сплиттера/экранирования, состоит в том, чтобы иметь какой-то алгоритм определения "обычного" числа столбцов в каждой строке, а затем проверять, в этом случае, поля фиксированной длины, такие как Ваша запись состояния AK или другой возможный ориентир как своего рода нормализация обратного останова для нонконформистских столбцов. Но эта серьезная сумасшедшая логика, которая, вероятно, не нужна, так же увлекательно, как и кодирование. Как указывает @Vash, вам лучше следовать некоторому стандарту и писать немного более осторожно.

Но проблема здесь, вероятно, проще, чем это. Единственный лексически значимый случай - это случай в вашем примере - ", - двойная кавычка, запятая, а затем пробел. Так, что проверяет код *** STINKY CONDITION ***. Несмотря на это, этот код становится противнее чем я хотел бы, что означает, что у вас есть когда-либо странные крайние случаи, такие как "This is also stinky," afab","Now what?" Черт, даже "A,"B","C" не работают в этом коде сейчас, iirc, так как я рассматриваю начальные и конечные символы как экранированные pre- и постфиксированные. вернуться к комментарию @Vash!

Извиняюсь за все скобки за однострочные операторы if, но я сейчас застрял в мире StyleCop. Я не обязательно предлагаю вам использовать это - то, что strictEscapeToSplitEvaluation плюс STINKY CONDITION делает это немного сложным. Но стоит иметь в виду, что обычный синтаксический анализатор csv, который разбирается в кавычках, значительно проще в плане утомительности, но в остальном тривиален.

namespace YourFavoriteNamespace 
{
    using System;
    using System.Collections.Generic;
    using System.Text;

    public static class Extensions
    {
        public static Queue<string> SplitSeeingQuotes(this string valToSplit, char splittingChar = ',', char escapeChar = '"', 
            bool strictEscapeToSplitEvaluation = true, bool captureEndingNull = false)
        {
            Queue<string> qReturn = new Queue<string>();
            StringBuilder stringBuilder = new StringBuilder();

            bool bInEscapeVal = false;

            for (int i = 0; i < valToSplit.Length; i++)
            {
                if (!bInEscapeVal)
                {
                    // Escape values must come immediately after a split.
                    // abc,"b,ca",cab has an escaped comma.
                    // abc,b"ca,c"ab does not.
                    if (escapeChar == valToSplit[i] && (!strictEscapeToSplitEvaluation || (i == 0 || (i != 0 && splittingChar == valToSplit[i - 1]))))
                    {
                        bInEscapeVal = true;    // not capturing escapeChar as part of value; easy enough to change if need be.
                    }
                    else if (splittingChar == valToSplit[i])
                    {
                        qReturn.Enqueue(stringBuilder.ToString());
                        stringBuilder = new StringBuilder();
                    }
                    else
                    {
                        stringBuilder.Append(valToSplit[i]);
                    }
                }
                else
                {
                    // Can't use switch b/c we're comparing to a variable, I believe.
                    if (escapeChar == valToSplit[i])
                    {
                        // Repeated escape always reduces to one escape char in this logic.
                        // So if you wanted "I'm ""double quote"" crazy!" to come out with 
                        // the double double quotes, you're toast.
                        if (i + 1 < valToSplit.Length && escapeChar == valToSplit[i + 1])
                        {
                            i++;
                            stringBuilder.Append(escapeChar);
                        }
                        else if (!strictEscapeToSplitEvaluation)
                        {
                            bInEscapeVal = false;
                        }
                        // *** STINKY CONDITION ***  
                        // Kinda defense, since only '", ' really makes sense.
                        else if ('"' == escapeChar && i + 2 < valToSplit.Length &&
                            valToSplit[i + 1] == ',' && valToSplit[i + 2] == ' ')
                        {
                            i = i+2;
                            stringBuilder.Append("\", ");
                        }
                        // *** EO STINKY CONDITION ***  
                        else if (i+1 == valToSplit.Length || (i + 1 < valToSplit.Length && valToSplit[i + 1] == splittingChar))
                        {
                            bInEscapeVal = false;
                        }
                        else
                        {
                            stringBuilder.Append(escapeChar);
                        }
                    }
                    else
                    {
                        stringBuilder.Append(valToSplit[i]);
                    }
                }
            }

            // NOTE: The 'captureEndingNull' flag is not tested.
            // Catch null final entry?  "abc,cab,bca," could be four entries, with the last an empty string.
            if ((captureEndingNull && splittingChar == valToSplit[valToSplit.Length-1]) || (stringBuilder.Length > 0))
            {
                qReturn.Enqueue(stringBuilder.ToString());
            }

            return qReturn;
        }
    }
}

Вероятно, стоит упомянуть, что в "ответе", который вы дали себе, нет проблемы "вонючего" в строке примера. ; ^)

[Понимая, что мы спустя три года после того, как вы спросили,] я скажу, что ваш пример не такой безумный, как это делают люди. Я вижу желание обрабатывать escape-символы (в данном случае, ") как escape-символы только в том случае, если они являются первым значением после разделительного символа или, после нахождения открывающего escape, останавливаются только в том случае, если вы находите escape-символ перед разделителем; в этом случае, сплиттер, очевидно ,.

Если строка вашего csv - abc,bc"a,ca"b, я ожидаю, что это означает, что у нас есть три значения: abc, bc"a и ca"b.

То же самое в вашей "The sample ("adasdad") asdada" - кавычки, которые не начинаются и не заканчиваются в значении ячейки, не являются escape-символами и не обязательно должны быть удвоены для сохранения значения. Таким образом, я добавил флаг strictEscapeToSplitEvaluation здесь.

Наслаждаться. ; ^)

Ответ 2

Я очень рекомендую использовать TextFieldParser. Ручные кодирующие парсеры, которые используют String.Split или регулярные выражения, почти неизменно смешивают такие вещи, как цитируемые поля, которые имеют встроенные кавычки или встроенные разделители.

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

Ответ 3

Разделить на

",

Я бы использовал MyString.IndexOf( "\", "

И затем подстройте части. Другое, что я уверен, что кто-то написал парсер csv, который может справиться с этим:)

Ответ 4

Я нашел способ разобрать этот неверный CSV. Я искал шаблон и нашел его... Я сначала заменил ( "," ) на символ... как "¤", а затем разделил его...

из этого:

"Annoying","CSV File","[email protected]",1999,01-20-2001,"oh,boy",01-20-2001,"yeah baby","yeah!"

:

"Annoying¤CSV File¤[email protected]",1999,01-20-2001,"oh,boy",01-20-2001,"yeah baby¤yeah!"

затем разделите его:

ArrayA[0]: "Annoying //this value will be trimmed by replace("\"","") same as the array[4]
ArrayA[1]: CSV File
ArrayA[2]: [email protected]",1999,01-20-2001,"oh,boy",01-20-2001,"yeah baby
ArrayA[3]: yeah!"

после его разделения я заменил строки из ArrayA [2] "и" с помощью ¤, а затем снова разложил

из этого

ArrayA[2]: [email protected]",1999,01-20-2001,"oh,boy",01-20-2001,"yeah baby

к этому

ArrayA[2]: [email protected]¤1999,01-20-2001¤oh,boy¤01-20-2001¤yeah baby

затем разбить его и обратиться к этому

ArrayB[0]: [email protected]
ArrayB[1]: 1999,01-20-2001
ArrayB[2]: oh,boy
ArrayB[3]: 01-20-2001
ArrayB[4]: yeah baby

и, наконец, я разделил только год и дату с ArrayB [1] с, до ArrayC

Это утомительно, но нет другого способа сделать это...

Ответ 5

Вы можете разбить строку на ",". Рекомендуется, чтобы файл csv мог содержать каждое значение ячейки в кавычках типа "1", "2", "3".....

Ответ 6

Я не понимаю, как вы можете, если каждая строка отличается. Эта строка неверна для CSV. Котировки, содержащиеся в значении, должны быть удвоены, как показано ниже. Я даже не могу точно сказать, где значения должны быть прекращены.

"1",1/2/2010,"The sample (""adasdad"") asdada","I was pooping in the door ""Stinky"", so I'll be damn","AK"

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

Ответ 7

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

Это не удастся, если ваши строки содержат кому. Чтобы этого избежать, цитаты нужно удвоить, как сказано в других ответах.

Ответ 8

Как нет (достойный).csv-парсер может правильно анализировать не-csv-данные, задача состоит не в анализе данных, а в исправлении файлов (а затем для анализа правильных данных).

Чтобы исправить данные, вам нужен список плохих строк (для отправки лицу, ответственному за мусор, для ручного редактирования). Чтобы получить такой список, вы можете

  • Используйте Access с правильной спецификацией импорта, чтобы импортировать файл. Вы получите список сбоев импорта.

  • напишите программу script/, которая открывает файл с помощью текстового драйвера OLEDB.

Пример файла:

"Id","Remark","DateDue"
1,"This is good",20110413
2,"This is ""good""",20110414
3,"This is ""good"","bad",and "ugly",,20110415
4,"This is ""good""" again,20110415

Пример SQL/Результат:

 SELECT * FROM [badcsv01.csv]
 Id Remark               DateDue   
  1 This is good         4/13/2011 
  2 This is "good"       4/14/2011 
  3 This is "good",        NULL    
  4 This is "good" again 4/15/2011 

SELECT * FROM [badcsv01.csv] WHERE DateDue Is Null
 Id Remark          DateDue 
  3 This is "good",  NULL