Как разделить csv, столбцы которого могут содержать,


дано

2,1016,7/31/2008 14:22,Dalgas, Который Джефф,6/5/2011 22:21,http://stackoverflow.com,"Корваллис, Орегон",7679,351,81,b437f461b3fd27387c5d8ab47a293d35,34

Как использовать C#, чтобы разделить вышеуказанную информацию на строки следующим образом:

2
1016
7/31/2008 14:22
Geoff Dalgas
6/5/2011 22:21
http://stackoverflow.com
Corvallis, OR
7679
351
81
b437f461b3fd27387c5d8ab47a293d35
34

Как вы можете видеть, один из столбцов содержит,

// обновление // На основе C# регулярное выражение Сплит-запятые снаружи цитаты

string[] result = Regex.Split(samplestring, ",(?=(?:[^"]*"[^"]*")*[^"]*$)");
8 67

8 ответов:

использовать Microsoft.VisualBasic.FileIO.TextFieldParser класса. Это будет обрабатывать разбор файла с разделителями,TextReader или Stream где некоторые поля заключены в кавычки, а некоторые нет.

например:

using Microsoft.VisualBasic.FileIO;

string csv = "2,1016,7/31/2008 14:22,Geoff Dalgas,6/5/2011 22:21,http://stackoverflow.com,\"Corvallis, OR\",7679,351,81,b437f461b3fd27387c5d8ab47a293d35,34";

TextFieldParser parser = new TextFieldParser(new StringReader(csv));

// You can also read from a file
// TextFieldParser parser = new TextFieldParser("mycsvfile.csv");

parser.HasFieldsEnclosedInQuotes = true;
parser.SetDelimiters(",");

string[] fields;

while (!parser.EndOfData)
{
    fields = parser.ReadFields();
    foreach (string field in fields)
    {
        Console.WriteLine(field);
    }
} 

parser.Close();

это должно привести к следующему результату:

2
1016
7/31/2008 14:22
Geoff Dalgas
6/5/2011 22:21
http://stackoverflow.com
Corvallis, OR
7679
351
81
b437f461b3fd27387c5d8ab47a293d35
34

посмотреть Microsoft.Визуальная основа.FileIO.TextFieldParser для получения дополнительной информации.

вам нужно добавить ссылку на Microsoft.VisualBasic на вкладке добавить ссылки .NET.

Это так поздно, но это может быть полезно для кого-то. Мы можем использовать регулярное выражение как ниже.

Regex CSVParser = new Regex(",(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))");
String[] Fields = CSVParser.Split(Test);

вы можете разделить на все запятые, которые имеют четное число кавычек после них.

вы также хотели бы посмотреть на specf для формата CSV об обработке запятой.

Полезные Ссылки : C# Regex Split - commas outside quotes

Я вижу, что если вы вставляете текст с разделителями csv в Excel и делаете "текст в Столбцы", он запрашивает у вас "текстовый квалификатор". Он по умолчанию содержит двойную кавычку, поэтому он обрабатывает текст в двойных кавычках как литерал. Я предполагаю, что Excel реализует это, переходя по одному символу за раз, если он встречает "текстовый квалификатор", он продолжает переходить к следующему "квалификатору". Вероятно, вы можете реализовать это самостоятельно с помощью цикла for и логического значения для обозначения, если вы находитесь внутри буквального текста.

public string[] CsvParser(string csvText)
{
    List<string> tokens = new List<string>();

    int last = -1;
    int current = 0;
    bool inText = false;

    while(current < csvText.Length)
    {
        switch(csvText[current])
        {
            case '"':
                inText = !inText; break;
            case ',':
                if (!inText) 
                {
                    tokens.Add(csvText.Substring(last + 1, (current - last)).Trim(' ', ',')); 
                    last = current;
                }
                break;
            default:
                break;
        }
        current++;
    }

    if (last != csvText.Length - 1) 
    {
        tokens.Add(csvText.Substring(last+1).Trim());
    }

    return tokens.ToArray();
}

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

Я создал метод ParseCsvRow () который возвращает массив из строки csv. Сначала я имею дело с двойными кавычками в строке, разбивая строку на двойные кавычки в массив quotesArray. Строка в кавычках. файлы CSV допустимо только при четном количестве двойных кавычек. Двойные кавычки в значении столбца должны быть заменены парой двойных кавычек (это подход Excel). Пока .csv-файл соответствует этим требованиям, вы можете ожидать, что запятые-разделители появятся только за пределами пар двойных кавычек. Запятые внутри пар двойных кавычек являются частью значения столбца и должны игнорироваться при разбиении .csv в массив.

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

    public static string[] ParseCsvRow(string csvrow)
    {
        const string obscureCharacter = "ᖳ";
        if (csvrow.Contains(obscureCharacter)) throw new Exception("Error: csv row may not contain the " + obscureCharacter + " character");

        var unicodeSeparatedString = "";

        var quotesArray = csvrow.Split('"');  // Split string on double quote character
        if (quotesArray.Length > 1)
        {
            for (var i = 0; i < quotesArray.Length; i++)
            {
                // CSV must use double quotes to represent a quote inside a quoted cell
                // Quotes must be paired up
                // Test if a comma lays outside a pair of quotes.  If so, replace the comma with an obscure unicode character
                if (Math.Round(Math.Round((decimal) i/2)*2) == i)
                {
                    var s = quotesArray[i].Trim();
                    switch (s)
                    {
                        case ",":
                            quotesArray[i] = obscureCharacter;  // Change quoted comma seperated string to quoted "obscure character" seperated string
                            break;
                    }
                }
                // Build string and Replace quotes where quotes were expected.
                unicodeSeparatedString += (i > 0 ? "\"" : "") + quotesArray[i].Trim();
            }
        }
        else
        {
            // String does not have any pairs of double quotes.  It should be safe to just replace the commas with the obscure character
            unicodeSeparatedString = csvrow.Replace(",", obscureCharacter);
        }

        var csvRowArray = unicodeSeparatedString.Split(obscureCharacter[0]); 

        for (var i = 0; i < csvRowArray.Length; i++)
        {
            var s = csvRowArray[i].Trim();
            if (s.StartsWith("\"") && s.EndsWith("\""))
            {
                csvRowArray[i] = s.Length > 2 ? s.Substring(1, s.Length - 2) : "";  // Remove start and end quotes.
            }
        }

        return csvRowArray;
    }

одним из недостатков моего подхода является то, как я временно заменяю разделительные запятые неясным символом unicode. Этот персонаж должен быть настолько неясным, что он никогда не появится в вашем .CSV-файл. Вы можете поставить больше обработки вокруг этого.

использовать библиотеку, как LumenWorks для чтения CSV. Он будет обрабатывать поля с кавычками в них и, вероятно, в целом будет более надежным, чем ваше пользовательское решение в силу того, что он был вокруг в течение длительного времени.

у меня была проблема с CSV, который содержит поля с символом кавычки в них, поэтому с помощью TextFieldParser я придумал следующее:

private static string[] parseCSVLine(string csvLine)
{
  using (TextFieldParser TFP = new TextFieldParser(new MemoryStream(Encoding.UTF8.GetBytes(csvLine))))
  {
    TFP.HasFieldsEnclosedInQuotes = true;
    TFP.SetDelimiters(",");

    try 
    {           
      return TFP.ReadFields();
    }
    catch (MalformedLineException)
    {
      StringBuilder m_sbLine = new StringBuilder();

      for (int i = 0; i < TFP.ErrorLine.Length; i++)
      {
        if (i > 0 && TFP.ErrorLine[i]== '"' &&(TFP.ErrorLine[i + 1] != ',' && TFP.ErrorLine[i - 1] != ','))
          m_sbLine.Append("\"\"");
        else
          m_sbLine.Append(TFP.ErrorLine[i]);
      }

      return parseCSVLine(m_sbLine.ToString());
    }
  }
}

StreamReader по-прежнему используется для чтения CSV строка за строкой, следующим образом:

using(StreamReader SR = new StreamReader(FileName))
{
  while (SR.Peek() >-1)
    myStringArray = parseCSVLine(SR.ReadLine());
}

С Cinchoo ETL - библиотека с открытым исходным кодом, она может автоматически обрабатывать значения столбцов, содержащих разделители.

string csv = @"2,1016,7/31/2008 14:22,Geoff Dalgas,6/5/2011 22:21,http://stackoverflow.com,""Corvallis, OR"",7679,351,81,b437f461b3fd27387c5d8ab47a293d35,34";

using (var p = ChoCSVReader.LoadText(csv)
    )
{
    Console.WriteLine(p.Dump());
}

выход:

Key: Column1 [Type: String]
Value: 2
Key: Column2 [Type: String]
Value: 1016
Key: Column3 [Type: String]
Value: 7/31/2008 14:22
Key: Column4 [Type: String]
Value: Geoff Dalgas
Key: Column5 [Type: String]
Value: 6/5/2011 22:21
Key: Column6 [Type: String]
Value: http://stackoverflow.com
Key: Column7 [Type: String]
Value: Corvallis, OR
Key: Column8 [Type: String]
Value: 7679
Key: Column9 [Type: String]
Value: 351
Key: Column10 [Type: String]
Value: 81
Key: Column11 [Type: String]
Value: b437f461b3fd27387c5d8ab47a293d35
Key: Column12 [Type: String]
Value: 34

для получения дополнительной информации, пожалуйста, посетите статью codeproject.

надеюсь, что это помогает.