Come dividere CSV le cui colonne possono contenere,

Dato

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

Come usare C # per suddividere le informazioni di cui sopra in stringhe come segue:

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

Come puoi vedere, una delle colonne contiene, <= (Corvallis, OR)

// update // Basato su C # Regex Split – virgolette al di fuori delle virgolette

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

Utilizzare la class Microsoft.VisualBasic.FileIO.TextFieldParser . Questo gestirà l’analisi di un file delimitato, TextReader o Stream dove alcuni campi sono racchiusi tra virgolette e altri no.

Per esempio:

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

Ciò dovrebbe comportare il seguente output:

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

Vedere Microsoft.VisualBasic.FileIO.TextFieldParser per ulteriori informazioni.

È necessario aggiungere un riferimento a Microsoft.VisualBasic nella scheda Aggiungi riferimenti .NET.

Puoi dividere su tutte le virgole che hanno un numero pari di quotazioni che le seguono.

Ti piacerebbe anche visualizzare le specf per il formato CSV sulla gestione delle virgole.

Link utile: C# Regex Split - commas outside quotes

È così tardi, ma questo può essere utile per qualcuno. Possiamo usare RegEx come muggito.

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

Vedo che se incolli il testo delimitato da csv in Excel e fai un “Testo in colonne”, ti chiede un “qualificatore di testo”. È impostato su un valore di virgola doppia per cui tratta il testo tra virgolette come letterale. Immagino che Excel implementa questo andando da un personaggio alla volta, se incontra un “qualificatore di testo”, continua ad andare al prossimo “qualificatore”. Probabilmente puoi implementarlo tu stesso con un ciclo for e un booleano per indicare se sei all’interno di un testo letterale.

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

È complicato analizzare i file .csv quando il file .csv può essere costituito da stringhe separate da virgole, da stringhe tra virgolette separate da virgole o da una combinazione caotica delle due. La soluzione che ho trovato consente una qualsiasi delle tre possibilità.

Ho creato un metodo, ParseCsvRow () che restituisce una matrice da una stringa csv. Prima mi occupo di virgolette doppie nella stringa suddividendo la stringa tra virgolette doppie in un array chiamato quotesArray. I file .csv con stringa citato sono validi solo se esiste un numero pari di doppi apici. Le doppie virgolette in un valore di colonna dovrebbero essere sostituite con una coppia di virgolette doppie (questo è l’approccio di Excel). Finché il file .csv soddisfa questi requisiti, puoi aspettarti che le virgole delimitatore appaiano solo al di fuori delle coppie di virgolette doppie. Le virgole all’interno di coppie di virgolette fanno parte del valore della colonna e devono essere ignorate quando si divide il file .csv in una matrice.

Il mio metodo testerà le virgole al di fuori delle coppie di doppi apici osservando solo gli indici pari delle virgoletteArray. Rimuove anche le virgolette doppie dall’inizio e dalla fine dei valori della colonna.

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

Uno svantaggio del mio approccio è il modo in cui sostituisco temporaneamente le virgole delimitatore con un oscuro carattere unicode. Questo personaggio deve essere così oscuro, non apparirebbe mai nel tuo file .csv. Si consiglia di mettere più attenzione su questo.

Utilizzare una libreria come LumenWorks per eseguire la lettura CSV. Gestirà i campi con le virgolette in essi contenuti e sarà probabilmente più robusto della soluzione personalizzata in virtù del fatto di essere in circolazione da molto tempo.

Ho avuto un problema con un CSV che contiene campi con un carattere di citazione in essi, quindi utilizzando TextFieldParser, ho trovato il seguente:

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

Un StreamReader è ancora utilizzato per leggere il file CSV riga per riga, come segue:

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

Con Cinchoo ETL , una libreria open source, può gestire automaticamente i valori delle colonne contenenti i separatori.

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

Produzione:

 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 

Per ulteriori informazioni, visitare l’articolo codeproject.

Spero che sia d’aiuto.