Bagaimana membagi csv yang kolomnya mungkin berisi,

105

Diberikan

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

Cara menggunakan C # untuk membagi informasi di atas menjadi string sebagai berikut:

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

Seperti yang Anda lihat salah satu kolom berisi, <= (Corvallis, OR)

// update // Berdasarkan C # Regex Split - koma di luar tanda kutip

string[] result = Regex.Split(samplestring, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)");
q0987
sumber
1
Meskipun di Jawa, Pertanyaan serupa: stackoverflow.com/questions/1757065/…
sgokhales
1
Menggunakan regex untuk melakukan ini adalah nasihat yang buruk. .NET Framework sudah memiliki dukungan bawaan untuk mengurai CSV. Lihat jawaban ini yang harus Anda terima. Jika tidak, saya akan menutup ini sebagai dupe dari stackoverflow.com/questions/3147836/… yang sama salahnya.
Kev
Bisakah Anda menjelaskan apa itu dukungan bawaan .NET untuk mengurai file CSV dengan koma yang disematkan? Apakah Anda mengacu pada kelas Microsoft.VisualBasic.FileIO.TextFieldParser?
AllSolutions

Jawaban:

182

Gunakan Microsoft.VisualBasic.FileIO.TextFieldParserkelas. Ini akan menangani penguraian file yang dipisahkan, TextReaderatau di Streammana beberapa bidang diapit tanda kutip dan beberapa tidak.

Sebagai contoh:

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

Ini akan menghasilkan keluaran berikut:

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

Lihat Microsoft.VisualBasic.FileIO.TextFieldParser untuk informasi selengkapnya.

Anda perlu menambahkan referensi ke Microsoft.VisualBasicdalam tab Add References .NET.

Tim
sumber
9
Sobat, terima kasih banyak untuk solusi ini, saya memiliki sekitar 500K + baris data CSV yang perlu saya muat ke dalam tabel dan dimuat dengan koma yang terdapat di dalam tanda kutip. Saya berhutang minuman dewasa pilihan Anda jika jalan kita pernah bersilangan.
Mark Kram
@ Tim saya menggunakan ini, dan pemberitahuannya melewatkan semua nomor baris genap, hanya memproses nomor baris ganjil dalam file yang memiliki 1050 baris. ada ide?
Smith
@Smith - Tanpa melihat kode atau input sampel Anda, saya tidak tahu. Saya sarankan memposting pertanyaan baru. Mungkin file tidak memiliki carriage return atau penanda akhir baris lainnya di garis genap?
Tim
Saya bahkan tidak tahu tentang perpustakaan ini sampai saya melihat ini - terima kasih! Jika ada orang lain yang menginginkan contoh yang mem-parsing seluruh file CSV, lihat jawaban SO ini: stackoverflow.com/a/3508572/3105807
Amy Barrett
2
Bisakah kita lynch Microsoft karena tidak menyediakan konstruktor yang mengambil string sehingga kita harus melewati lingkaran untuk mengubahnya menjadi aliran terlebih dahulu ?? Jika tidak, jawaban yang bagus.
Loren Pechtel
43

Ini sudah sangat terlambat tetapi ini bisa membantu seseorang. Kita bisa menggunakan RegEx seperti di bawah ini.

Regex CSVParser = new Regex(",(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))");
String[] Fields = CSVParser.Split(Test);
Husen
sumber
4
Ini sempurna. Lebih suka menggunakan ini daripada mengimpor seluruh pustaka lainnya. Bravo.
TheGeekYouNeed
1
Cocok dengan asdf, "", "sebagai ,\" df ",
Solusi ini tidak berfungsi dengan benar - tidak memperhitungkan tanda ucapan, artinya akan ada banyak tanda ucapan di lokasi yang salah selama membaca.
AidanH
Bagaimana jika kutipan akhir tidak ada di beberapa baris: asd, "", "as, \" df "," asd asd "," as
MarmiK
1
Ini berhasil bagi saya dan termasuk tanda-tanda pidato yang dikutip. 30 juta barisnya. Sangat bagus dan sedikit kode.
GBGOLC
4

Saya melihat bahwa jika Anda menempelkan teks dipisahkan csv di Excel dan melakukan "Teks ke Kolom", Anda diminta untuk "pengualifikasi teks". Ini secara default menggunakan tanda kutip ganda sehingga memperlakukan teks dalam tanda kutip ganda sebagai literal. Saya membayangkan bahwa Excel mengimplementasikan ini dengan pergi satu karakter pada satu waktu, jika bertemu dengan "kualifikasi teks", itu terus berlanjut ke "kualifikasi" berikutnya. Anda mungkin dapat mengimplementasikannya sendiri dengan for loop dan boolean untuk menunjukkan jika Anda berada di dalam teks literal.

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

Gunakan perpustakaan seperti LumenWorks untuk melakukan pembacaan CSV Anda. Ini akan menangani bidang dengan tanda kutip di dalamnya dan kemungkinan akan secara keseluruhan lebih kuat daripada solusi khusus Anda karena telah ada untuk waktu yang lama.

Adam Lear
sumber
2

Merupakan masalah yang sulit untuk mengurai file .csv jika file .csv dapat berupa string yang dipisahkan koma, string yang dikutip dipisahkan koma, atau kombinasi yang kacau dari keduanya. Solusi yang saya dapatkan memungkinkan untuk salah satu dari tiga kemungkinan.

Saya membuat metode, ParseCsvRow () yang mengembalikan array dari string csv. Saya pertama kali berurusan dengan tanda kutip ganda dalam string dengan memisahkan string pada tanda kutip ganda menjadi sebuah array yang disebut quotesArray. File .csv string yang diberi tanda kutip hanya valid jika terdapat jumlah tanda kutip ganda yang genap. Tanda kutip ganda dalam nilai kolom harus diganti dengan sepasang tanda kutip ganda (Ini adalah pendekatan Excel). Selama file .csv memenuhi persyaratan ini, Anda dapat mengharapkan koma pembatas muncul hanya di luar pasangan tanda kutip ganda. Koma di dalam pasangan tanda kutip ganda adalah bagian dari nilai kolom dan harus diabaikan saat memisahkan .csv menjadi sebuah larik.

Metode saya akan menguji koma di luar pasangan kutipan ganda dengan hanya melihat indeks genap dari quotesArray. Ini juga menghapus tanda kutip ganda dari awal dan akhir nilai kolom.

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

Satu kelemahan dari pendekatan saya adalah cara saya mengganti sementara koma pembatas dengan karakter unicode yang tidak jelas. Karakter ini harus sangat tidak jelas, sehingga tidak akan pernah muncul di file .csv Anda. Anda mungkin ingin menangani lebih banyak hal ini.

Jason Williams
sumber
1

Saya memiliki masalah dengan CSV yang berisi bidang dengan karakter kutipan di dalamnya, jadi dengan menggunakan TextFieldParser, saya menemukan yang berikut:

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 masih digunakan untuk membaca CSV baris demi baris, sebagai berikut:

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

Dengan Cinchoo ETL - pustaka sumber terbuka, ia dapat secara otomatis menangani nilai kolom yang berisi pemisah.

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

Keluaran:

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

Untuk informasi lebih lanjut, silakan kunjungi artikel codeproject.

Semoga membantu.

RajN
sumber