วิธีแยก csv ที่อาจมีคอลัมน์


107

ให้

2,1016,7 / 31/2008 14: 22, Geoff Dalgas, 6/5/2011 22:21, http://stackoverflow.com , "Corvallis, หรือ", 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

ดังที่คุณเห็นหนึ่งในคอลัมน์ประกอบด้วย <= (Corvallis หรือ)

// update // อิงตาม C # Regex Split - เครื่องหมายจุลภาคนอกเครื่องหมายคำพูด

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

1
แม้ว่าจะอยู่ใน Java แต่คำถามที่คล้ายกัน: stackoverflow.com/questions/1757065/…
sgokhales

1
การใช้ regex เพื่อทำสิ่งนี้เป็นคำแนะนำที่ไม่ดี .NET Framework มีการสนับสนุนในตัวเพื่อแยกวิเคราะห์ CSV แล้ว ดูคำตอบนี้ซึ่งเป็นคำตอบที่คุณควรยอมรับ ไม่เช่นนั้นฉันจะปิดสิ่งนี้เป็นการหลอกลวงstackoverflow.com/questions/3147836/…ซึ่งผิดพอ ๆ กัน
Kev

คุณช่วยอธิบายได้ไหมว่าอะไรคือการสนับสนุนในตัวของ. NET สำหรับการแยกวิเคราะห์ไฟล์ CSV ด้วยเครื่องหมายจุลภาคในตัว คุณกำลังอ้างถึงคลาส Microsoft.VisualBasic.FileIO.TextFieldParser หรือไม่
AllSolutions

คำตอบ:


183

ใช้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
31/7/2561 14:22 น
Geoff Dalgas
5/6/2554 22:21 น
http://stackoverflow.com
Corvallis หรือ
7679
351
81
b437f461b3fd27387c5d8ab47a293d35
34

ดูMicrosoft.VisualBasic.FileIO.TextFieldParserสำหรับข้อมูลเพิ่มเติม

คุณต้องเพิ่มข้อมูลอ้างอิงMicrosoft.VisualBasicในแท็บ Add References .NET


9
ขอบคุณมากสำหรับวิธีแก้ปัญหานี้ฉันมีข้อมูล CSV ประมาณ 500K + แถวที่ฉันต้องโหลดลงในตารางและโหลดด้วยเครื่องหมายจุลภาคที่อยู่ในเครื่องหมายคำพูด ฉันเป็นหนี้คุณสำหรับเครื่องดื่มสำหรับผู้ใหญ่ที่คุณเลือกหากเส้นทางของเราข้ามไป
Mark Kram

@tim ฉันใช้สิ่งนี้และสังเกตเห็นว่ามันข้ามหมายเลขบรรทัดคู่ทั้งหมดเพียงประมวลผลหมายเลขบรรทัดคี่ในไฟล์ที่มี 1,050 บรรทัด ความคิดใด ๆ
Smith

@Smith - โดยไม่เห็นรหัสหรือตัวอย่างการป้อนข้อมูลของคุณฉันไม่รู้ ฉันขอแนะนำให้โพสต์คำถามใหม่ บางทีไฟล์อาจไม่มีการส่งคืนแคร่หรือเครื่องหมายจุดสิ้นสุดของบรรทัดอื่น ๆ บนเส้นคู่?
ทิม

ฉันไม่รู้เกี่ยวกับห้องสมุดนี้เลยจนกระทั่งได้เห็นสิ่งนี้ - ขอบคุณ! หากใครต้องการตัวอย่างที่แยกวิเคราะห์ไฟล์ CSV ทั้งหมดโปรดดูคำตอบ SO นี้: stackoverflow.com/a/3508572/3105807
Amy Barrett

2
เราสามารถประชาทัณฑ์ Microsoft เพื่อไม่ให้ตัวสร้างที่ใช้สตริงดังนั้นเราจึงต้องข้ามห่วงของการแปลงเป็นสตรีมก่อนหรือไม่? มิฉะนั้นคำตอบที่ดี
Loren Pechtel

43

มันสายมาก แต่อาจเป็นประโยชน์สำหรับใครบางคน เราสามารถใช้ RegEx ร้องได้

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

4
เหมาะมากครับ ต้องการใช้สิ่งนี้มากกว่าการนำเข้าไลบรารีอื่นทั้งหมด ไชโย
TheGeekYouNeed

1
ตรงกับasdf, "", "เป็น,\" df ",

โซลูชันนี้ทำงานไม่ถูกต้อง - ไม่มีเครื่องหมายคำพูดซึ่งหมายความว่าจะมีเครื่องหมายคำพูดจำนวนมากในตำแหน่งที่ไม่ถูกต้องระหว่างการอ่าน
AidanH

จะเกิดอะไรขึ้นถ้าไม่มีเครื่องหมายคำพูดลงท้ายในบางบรรทัด: asd, "", "as, \" df "," asd asd "," as
MarmiK

1
สิ่งนี้ใช้ได้ผลสำหรับฉันและคิดเป็นเครื่องหมายคำพูดที่ยกมา 30 ล้านแถว รหัสที่ดีมากและน้อยที่สุด
GBGOLC

4

คุณสามารถแบ่งเครื่องหมายจุลภาคทั้งหมดที่มีเครื่องหมายอัญประกาศเป็นเลขคู่ตามหลังได้

คุณยังต้องการดูในspecfรูปแบบ CSV เกี่ยวกับการจัดการลูกน้ำ

ลิงค์ที่เป็นประโยชน์: C# Regex Split - commas outside quotes


3
@ q0987 - นี่ไม่ใช่คำตอบที่ถูกต้อง มีการสนับสนุนสิ่งนี้ใน Framework: stackoverflow.com/questions/6542996/…
Kev

4

ฉันเห็นว่าถ้าคุณวางข้อความที่คั่นด้วย csv ใน Excel และสร้าง "Text to Columns" ระบบจะขอ "text qualifier" ค่าเริ่มต้นเป็นเครื่องหมายคำพูดคู่เพื่อให้ถือว่าข้อความภายในอัญประกาศเป็นตัวอักษร ฉันจินตนาการว่า Excel ใช้สิ่งนี้โดยไปทีละอักขระหากพบ "text qualifier" ก็จะเข้าสู่ "qualifier" ถัดไป คุณสามารถใช้สิ่งนี้ได้ด้วยตัวเองโดยใช้ for loop และบูลีนเพื่อแสดงว่าคุณอยู่ภายในข้อความตามตัวอักษรหรือไม่

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

ใช้ไลบรารีเช่นLumenWorksเพื่ออ่าน CSV ของคุณ มันจะจัดการกับฟิลด์ที่มีเครื่องหมายคำพูดอยู่ในนั้นและโดยรวมแล้วจะมีประสิทธิภาพมากกว่าโซลูชันที่กำหนดเองของคุณเนื่องจากมีมานานแล้ว


2

เป็นเรื่องยุ่งยากในการแยกวิเคราะห์ไฟล์. csv เมื่อไฟล์. csv อาจเป็นสตริงที่คั่นด้วยเครื่องหมายจุลภาคสตริงที่มีเครื่องหมายอัญประกาศที่คั่นด้วยจุลภาคหรือการรวมกันของทั้งสองอย่างวุ่นวาย วิธีแก้ปัญหาที่ฉันคิดขึ้นช่วยให้มีความเป็นไปได้สามอย่าง

ฉันสร้างเมธอด ParseCsvRow () ซึ่งส่งคืนอาร์เรย์จากสตริง csv ก่อนอื่นฉันจัดการกับเครื่องหมายคำพูดคู่ในสตริงโดยการแยกสตริงของเครื่องหมายคำพูดคู่ออกเป็นอาร์เรย์ที่เรียกว่า quotesArray ไฟล์. csv สตริงที่ยกมาจะใช้ได้ก็ต่อเมื่อมีเครื่องหมายคำพูดคู่เป็นจำนวน คำพูดคู่ในค่าคอลัมน์ควรแทนที่ด้วยเครื่องหมายคำพูดคู่ (นี่คือแนวทางของ Excel) ตราบใดที่ไฟล์. csv ตรงตามข้อกำหนดเหล่านี้คุณสามารถคาดหวังว่าเครื่องหมายจุลภาคตัวคั่นจะปรากฏเฉพาะนอกเครื่องหมายคำพูดคู่ เครื่องหมายจุลภาคภายในคู่ของอัญประกาศคู่เป็นส่วนหนึ่งของค่าคอลัมน์และควรละเว้นเมื่อแยก. csv ออกเป็นอาร์เรย์

วิธีการของฉันจะทดสอบเครื่องหมายจุลภาคนอกคู่อัญประกาศโดยดูเฉพาะดัชนีคู่ของอัญประกาศ นอกจากนี้ยังลบเครื่องหมายคำพูดคู่ออกจากค่าเริ่มต้นและจุดสิ้นสุดของคอลัมน์

    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 ของคุณ คุณอาจต้องการจัดการกับสิ่งนี้มากขึ้น


1

ฉันมีปัญหากับ 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());
}

1

ด้วย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

หวังว่าจะช่วยได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.