Cách tách csv có các cột có thể chứa,


105

Đượ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

Cách sử dụng C # để chia thông tin trên thành các chuỗi như sau:

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

Như bạn có thể thấy một trong các cột chứa, <= (Corvallis, OR)

// cập nhật // Dựa trên C # Regex Split - dấu phẩy bên ngoài dấu ngoặc kép

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

1
Mặc dù trong Java, Câu hỏi tương tự: stackoverflow.com/questions/1757065/...
sgokhales

1
Sử dụng regex để làm điều này là một lời khuyên tồi. .NET Framework đã có sẵn hỗ trợ để phân tích cú pháp CSV. Hãy xem câu trả lời này là câu bạn nên chấp nhận. Nếu không, tôi sẽ đóng nó dưới dạng bản dupe của stackoverflow.com/questions/3147836/… , điều này cũng sai như nhau.
Kev

Bạn có thể vui lòng giải thích rõ hỗ trợ tích hợp sẵn của .NET để phân tích cú pháp tệp CSV bằng dấu phẩy được nhúng không? Bạn đang đề cập đến lớp Microsoft.VisualBasic.FileIO.TextFieldParser?
AllSolutions,

Câu trả lời:


182

Sử dụng Microsoft.VisualBasic.FileIO.TextFieldParserlớp học. Điều này sẽ xử lý phân tích cú pháp một tệp được phân tách, TextReaderhoặcStream trong đó một số trường được đặt trong dấu ngoặc kép và một số trường thì không.

Ví dụ:

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

Điều này sẽ dẫn đến kết quả sau:

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

Xem Microsoft.VisualBasic.FileIO.TextFieldParser để biết thêm thông tin.

Bạn cần thêm tham chiếu đến Microsoft.VisualBasictrong tab Thêm tham chiếu .NET.


9
Anh bạn, cảm ơn bạn rất nhiều về giải pháp này, tôi có khoảng 500K + hàng dữ liệu CSV mà tôi cần tải vào một bảng và nó được tải bằng dấu phẩy bên trong dấu ngoặc kép. Tôi nợ bạn một loại đồ uống dành cho người lớn do bạn lựa chọn nếu con đường của chúng ta đi ngang qua.
Mark Kram

@tim tôi đã sử dụng điều này và nhận thấy nó bỏ qua tất cả các số dòng chẵn, chỉ xử lý các số dòng lẻ trong một tệp có 1050 dòng. bất kỳ ý tưởng?
Smith

@Smith - Không thấy mã hoặc mẫu nhập của bạn, tôi không biết. Tôi đề nghị đăng một câu hỏi mới. Có thể tệp thiếu dấu xuống dòng hoặc dấu cuối dòng khác trên các dòng chẵn?
Tim

Tôi thậm chí còn không biết về thư viện này cho đến khi tôi nhìn thấy nó - cảm ơn! Nếu bất kỳ ai khác muốn một ví dụ phân tích cú pháp toàn bộ tệp CSV, hãy xem câu trả lời SO này: stackoverflow.com/a/3508572/3105807
Amy Barrett

2
Chúng ta có thể giải thích cho Microsoft vì không cung cấp một hàm tạo lấy một chuỗi để chúng ta phải chuyển qua vòng chuyển đổi nó thành một luồng trước không ?? Nếu không, câu trả lời hay.
Loren Pechtel

43

Tuy đã muộn nhưng điều này có thể hữu ích cho ai đó. Chúng ta có thể sử dụng RegEx như dưới đây.

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

4
Đây là hoàn hảo. Thà sử dụng cái này hơn là nhập toàn bộ thư viện khác. Hoan hô.
TheGeekYouNeed

1
Đối sánh asdf, "", "as ,\" df ",

Giải pháp này không hoạt động chính xác - nó không tính đến dấu giọng nói, nghĩa là sẽ có rất nhiều dấu giọng nói ở các vị trí không chính xác trong quá trình đọc.
AidanH

Điều gì sẽ xảy ra nếu dấu trích dẫn kết thúc bị thiếu trong một số dòng: asd, "", "as, \" df "," asd asd "," as
MarmiK

1
Điều này đã làm việc cho tôi và tính đến các điểm bài phát biểu được trích dẫn. 30 triệu hàng trong số đó. Rất tốt và số lượng mã tối thiểu.
GBGOLC


4

Tôi thấy rằng nếu bạn dán văn bản được phân tách bằng csv trong Excel và thực hiện "Văn bản thành cột", nó sẽ yêu cầu bạn nhập "bộ định nghĩa văn bản". Nó được mặc định là một dấu ngoặc kép để nó coi văn bản trong dấu ngoặc kép là chữ. Tôi tưởng tượng rằng Excel thực hiện điều này bằng cách đi một ký tự tại một thời điểm, nếu nó gặp phải "bộ định tính văn bản", nó sẽ tiếp tục chuyển sang "bộ định tính" tiếp theo. Bạn có thể tự thực hiện điều này bằng vòng lặp for và boolean để biểu thị nếu bạn đang ở trong văn bản theo nghĩa đen.

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

Sử dụng thư viện như LumenWorks để đọc CSV của bạn. Nó sẽ xử lý các trường có dấu ngoặc kép trong đó và về tổng thể sẽ mạnh mẽ hơn giải pháp tùy chỉnh của bạn do đã có từ lâu.


2

Việc phân tích cú pháp tệp .csv là một vấn đề phức tạp khi tệp .csv có thể là các chuỗi được phân tách bằng dấu phẩy, các chuỗi được trích dẫn được phân tách bằng dấu phẩy hoặc kết hợp hỗn loạn của cả hai. Giải pháp mà tôi đưa ra cho phép bất kỳ khả năng nào trong ba khả năng.

Tôi đã tạo một phương thức, ParseCsvRow () trả về một mảng từ một chuỗi csv. Đầu tiên tôi xử lý dấu ngoặc kép trong chuỗi bằng cách tách chuỗi trên dấu ngoặc kép thành một mảng có tên là quoteArray. Các tệp .csv trong chuỗi được trích dẫn chỉ hợp lệ nếu có một số chẵn trong dấu ngoặc kép. Dấu ngoặc kép trong giá trị cột nên được thay thế bằng một cặp dấu ngoặc kép (Đây là cách tiếp cận của Excel). Miễn là tệp .csv đáp ứng các yêu cầu này, bạn có thể mong đợi các dấu phẩy phân tách chỉ xuất hiện bên ngoài các cặp dấu ngoặc kép. Dấu phẩy bên trong các cặp dấu ngoặc kép là một phần của giá trị cột và nên được bỏ qua khi tách .csv thành một mảng.

Phương pháp của tôi sẽ kiểm tra dấu phẩy bên ngoài cặp dấu ngoặc kép bằng cách chỉ xem xét các chỉ mục chẵn của dấu ngoặc kép. Nó cũng loại bỏ dấu ngoặc kép khỏi giá trị đầu và cuối cột.

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

Một nhược điểm của cách tiếp cận của tôi là cách tôi tạm thời thay thế dấu phẩy phân cách bằng một ký tự unicode khó hiểu. Ký tự này cần phải thật tối nghĩa, nó sẽ không bao giờ hiển thị trong tệp .csv của bạn. Bạn có thể muốn xử lý thêm vấn đề này.


1

Tôi đã gặp sự cố với CSV có chứa các trường có ký tự trích dẫn trong đó, vì vậy bằng cách sử dụng TextFieldParser, tôi đã tìm ra những điều sau:

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 vẫn được sử dụng để đọc từng dòng CSV, như sau:

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

1

Với Cinchoo ETL - một thư viện mã nguồn mở, nó có thể tự động xử lý các giá trị cột chứa dấu phân cách.

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

Đầu ra:

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

Để biết thêm thông tin, vui lòng truy cập bài viết codeproject.

Hy vọng nó giúp.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.