Como dividir o csv cujas colunas podem conter

Dado

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

Como usar c # para dividir as informações acima em seqüências de caracteres da seguinte maneira:

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

Como você pode ver, uma das colunas contém <= (Corvallis, OR)

// update // Baseado em C # Regex Split – vírgulas fora das aspas

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

Use a class Microsoft.VisualBasic.FileIO.TextFieldParser . Isso manipulará a análise de um arquivo delimitado, TextReader ou Stream que alguns campos são colocados entre aspas e outros não.

Por exemplo:

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

Isso deve resultar na seguinte saída:

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

Consulte Microsoft.VisualBasic.FileIO.TextFieldParser para obter mais informações.

Você precisa adicionar uma referência ao Microsoft.VisualBasic na guia Add References .NET.

Você pode dividir em todas as vírgulas que têm um número par de citações seguindo-as.

Você também gostaria de ver no specf para o formato CSV sobre o tratamento de vírgula.

Link útil: C# Regex Split - commas outside quotes

É muito tarde, mas isso pode ser útil para alguém. Podemos usar o RegEx como abaixo.

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

Vejo que, se você colar o texto delimitado por csv no Excel e fizer um “Text to Columns”, ele solicitará um “qualificador de texto”. É padronizado com uma aspa dupla para que ele trate o texto entre aspas duplas como literal. Eu imagino que o Excel implementa isso indo um caractere de cada vez, se ele encontrar um “qualificador de texto”, ele continua indo para o próximo “qualificador”. Você provavelmente pode implementar isso sozinho com um loop for e um booleano para indicar se você está dentro do texto literal.

 public string[] CsvParser(string csvText) { List tokens = new List(); 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(); } 

É complicado analisar arquivos .csv quando o arquivo .csv pode ser cadeias separadas por vírgula, cadeias de caracteres separadas por vírgula ou uma combinação caótica das duas. A solução que criei permite qualquer uma das três possibilidades.

Eu criei um método, ParseCsvRow () que retorna uma matriz de uma seqüência de csv. Primeiro lidei com aspas duplas na string dividindo a string entre aspas duplas em uma matriz chamada quotesArray. Arquivos .csv de string citados são válidos somente se houver um número par de aspas duplas. Aspas duplas em um valor de coluna devem ser substituídas por um par de aspas duplas (essa é a abordagem do Excel). Contanto que o arquivo .csv atenda a esses requisitos, você pode esperar que as vírgulas delimitadoras apareçam apenas fora dos pares de aspas duplas. Vírgulas dentro de pares de aspas duplas fazem parte do valor da coluna e devem ser ignoradas ao dividir o arquivo .csv em uma matriz.

Meu método testará as vírgulas fora dos pares de aspas duplas observando apenas os índices pares do aspas. Também remove aspas duplas dos valores inicial e final da coluna.

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

Uma desvantagem da minha abordagem é a maneira como eu substituo temporariamente as vírgulas delimitadoras por um caractere obscuro unicode. Este personagem precisa ser tão obscuro, ele nunca apareceria no seu arquivo .csv. Você pode querer colocar mais controle sobre isso.

Use uma biblioteca como o LumenWorks para fazer sua leitura em CSV. Ele lidará com campos com citações neles e provavelmente será mais robusto do que sua solução personalizada em virtude de ter estado por aí por um longo tempo.

Eu tive um problema com um CSV que contém campos com um caractere de citação, então usando o TextFieldParser, eu criei o seguinte:

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

Um StreamReader ainda é usado para ler o CSV linha por linha, da seguinte maneira:

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

Com o Cinchoo ETL – uma biblioteca de código aberto, ele pode lidar automaticamente com valores de colunas contendo separadores.

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

Saída:

 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 

Para mais informações, visite o artigo codeproject.

Espero que ajude.