Sütunları içerebilecek csv nasıl bölünür,


105

Verilen

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

Yukarıdaki bilgileri dizelere ayırmak için C # nasıl kullanılır?

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

Gördüğünüz gibi sütunlardan biri <= (Corvallis, OR) içeriyor

// güncelleme // C # Regex Split'e göre - tırnakların dışındaki virgüller

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

1
Java'da olsa da, benzer Soru: stackoverflow.com/questions/1757065/…
sgokhales

1
Bunu yapmak için bir normal ifade kullanmak kötü bir tavsiye. .NET Framework zaten CSV'yi ayrıştırmak için yerleşik desteğe sahiptir. Kabul etmeniz gereken bu cevaba bakın. Aksi takdirde, bunu stackoverflow.com/questions/3147836/… dosyasının bir kopyası olarak kapatırım ki bu da aynı derecede yanlıştır.
Kev

.NET'in CSV dosyalarını katıştırılmış virgüllerle ayrıştırmak için yerleşik desteğinin ne olduğunu açıklayabilir misiniz? Microsoft.VisualBasic.FileIO.TextFieldParser sınıfına mı başvuruyorsunuz?
AllSolutions

Yanıtlar:


182

Microsoft.VisualBasic.FileIO.TextFieldParserSınıfı kullanın . Bu, sınırlandırılmış bir dosyanın ayrıştırılmasını TextReaderveya Streambazı alanların tırnak içinde olduğu ve bazılarının bulunmadığı durumlarda işleyecektir .

Örneğin:

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

Bu, aşağıdaki çıktıyla sonuçlanmalıdır:

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

Daha fazla bilgi için Microsoft.VisualBasic.FileIO.TextFieldParser konusuna bakın .

Microsoft.VisualBasicReferans Ekle .NET sekmesine bir referans eklemeniz gerekir .


9
Dostum, bu çözüm için çok teşekkür ederim, bir tabloya yüklemem gereken yaklaşık 500K + satır CSV verisi var ve tırnak içinde bulunan virgüllerle yüklendi. Yollarımız kesişirse, sana seçeceğin bir yetişkin içeceği borçluyum.
Mark Kram

@tim bunu kullandım ve tüm çift satır numaralarını atladığını fark etti, sadece 1050 satırlık bir dosyadaki tek satır numaralarını işledi. herhangi bir fikir?
Smith

@Smith - Kodunuzu veya örnek girişinizi görmeden hiçbir fikrim yok. Yeni bir soru göndermenizi öneririm. Belki dosyada bir satır başı işareti veya çift satırlarda başka bir satır sonu işareti eksik olabilir?
Tim

Bunu görene kadar bu kütüphaneden haberim bile yoktu - teşekkürler! Başka biri bütün bir CSV dosyasını ayrıştıran bir örnek isterse, şu SO cevabına bakın: stackoverflow.com/a/3508572/3105807
Amy Barrett

2
Microsoft'u, bir dizge alan bir kurucu sağlamadığı için linç edebilir miyiz, bu yüzden önce onu bir akışa dönüştürme çemberinden atlamamız gerekir mi? Aksi takdirde, güzel cevap.
Loren Pechtel

43

Çok geç oldu ama bu birisi için faydalı olabilir. RegEx'i aşağıdaki gibi kullanabiliriz.

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

4
Bu harika. Başka bir kitaplığı içe aktarmaktansa bunu kullanmayı tercih eder. Bravo.
TheGeekYouNeed

1
Eşleşmeler olarak "" "asdf ,\" df",

Bu çözüm doğru çalışmıyor - konuşma işaretlerini hesaba katmıyor, bu da okuma sırasında yanlış yerlerde çok sayıda konuşma işareti olacağı anlamına geliyor.
AidanH

Ya bir satırda son alıntı eksikse: asd, "", "as, \" df "," asd asd "," as
MarmiK

1
Bu benim için çalıştı ve alıntılanmış konuşma işaretlerini hesaba kattı. 30 milyon sıra. Çok iyi ve minimum miktarda kod.
GBGOLC


4

Excel'de csv ile ayrılmış bir metin yapıştırıp bir "Metni Sütunlara" yaparsanız, bunun sizden bir "metin niteleyicisi" istediğini görüyorum. Çift tırnak içindeki metni değişmez olarak ele alması için varsayılan olarak çift tırnak kullanılır. Excel'in bunu her seferinde bir karakter giderek uyguladığını, bir "metin niteleyicisi" ile karşılaşırsa, bir sonraki "niteleyiciye" gitmeye devam ettiğini düşünüyorum. Metnin içinde olup olmadığınızı belirtmek için muhtemelen bunu kendiniz bir for döngüsü ve bir boole ile uygulayabilirsiniz.

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

3

CSV okumanızı yapmak için LumenWorks gibi bir kitaplık kullanın . İçlerinde tırnak bulunan alanları ele alacak ve uzun süredir ortalıkta olduğu için özel çözümünüzden genel olarak daha sağlam olacaktır.


2

.Csv dosyası virgülle ayrılmış dizeler, virgülle ayrılmış alıntı dizeler veya ikisinin kaotik bir kombinasyonu olabileceği zaman .csv dosyalarını ayrıştırmak zor bir konudur. Bulduğum çözüm, üç olasılıktan herhangi birine izin veriyor.

Bir csv dizesinden bir dizi döndüren ParseCsvRow () adlı bir yöntem oluşturdum. İlk önce çift tırnak üzerindeki dizeyi quotesArray adlı bir diziye bölerek dizedeki çift tırnakları ele alıyorum. Alıntılanan dize .csv dosyaları yalnızca çift tırnak işareti varsa geçerlidir. Bir sütun değerindeki çift tırnak, bir çift çift tırnak ile değiştirilmelidir (Bu, Excel'in yaklaşımıdır). .Csv dosyası bu gereksinimleri karşıladığı sürece, ayırıcı virgüllerin yalnızca çift tırnak çiftlerinin dışında görünmesini bekleyebilirsiniz. Çift tırnak çiftlerinin içindeki virgüller sütun değerinin bir parçasıdır ve .csv bir diziye bölünürken göz ardı edilmelidir.

Yöntemim, yalnızca quotesArray'in çift dizinlerine bakarak çift tırnak çiftlerinin dışındaki virgülleri test edecek. Ayrıca, sütun değerlerinin başındaki ve sonundaki çift tırnak işaretlerini kaldırır.

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

Yaklaşımımın bir dezavantajı, sınırlayıcı virgüllerini belirsiz bir unicode karakterle geçici olarak değiştirme şeklimdir. Bu karakterin o kadar belirsiz olması gerekir ki .csv dosyanızda asla görünmez. Bununla daha fazla ilgilenmek isteyebilirsiniz.


1

İçinde alıntı karakteri olan alanlar içeren bir CSV ile ilgili bir sorun yaşadım, bu yüzden TextFieldParser'ı kullanarak aşağıdakileri buldum:

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

Bir StreamReader, aşağıdaki gibi CSV'yi satır satır okumak için hala kullanılmaktadır:

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

1

İle Cinchoo ETL - açık kaynak kütüphane, otomatik olarak kolları sütunlar değerleri ayırıcılar içeren olabilir.

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

Çıktı:

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

Daha fazla bilgi için lütfen codeproject makalesini ziyaret edin.

Umarım yardımcı olur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.