วิธีอ่านไฟล์ CSV ลงใน. NET Data


170

ฉันจะโหลดไฟล์ CSV ลงใน a System.Data.DataTableสร้างข้อมูลได้อย่างไรโดยอ้างอิงจากไฟล์ CSV

ฟังก์ชั่นปกติของ ADO.net อนุญาตหรือไม่


21
สิ่งนี้อาจเป็น 'หัวข้อปิด' ได้อย่างไร เป็นคำถามที่เฉพาะเจาะจงและมีคน 100 คนพบว่ามีประโยชน์
Ryan

10
@ Ryan: แน่นอนฉันพูดกับคุณ ... ผู้ดูแล StackOverflow เป็นฝูงของงูพิษ รับฉันไว้ข้างหลังผู้ดูแล StackOverflow!
Ronnie Overby

คำตอบ:


89

นี่คือคลาสที่ยอดเยี่ยมที่จะคัดลอกข้อมูล CSV ลงใน DataTable โดยใช้โครงสร้างของข้อมูลเพื่อสร้าง DataTable:

ตัวแยกวิเคราะห์ทั่วไปแบบพกพาและมีประสิทธิภาพสำหรับไฟล์แบน

ง่ายต่อการกำหนดค่าและใช้งานง่าย ฉันอยากให้คุณดู


ยอดเยี่ยมแน่นอน มันทำงานได้อย่างสมบูรณ์แบบสำหรับฉันโดยไม่ต้องอ่านเอกสาร
smirkingman

สิ่งนี้จะทำงานกับไฟล์ CSV ที่แต่ละแถวอาจมีโครงสร้างที่แตกต่างกันหรือไม่ ฉันมีไฟล์บันทึกที่มีประเภทบันทึกเหตุการณ์ที่แตกต่างกันซึ่งจะต้องแยกออกเป็นหลายตาราง
gonzobrains

2
@gonzobrains - อาจไม่; ข้อสมมติฐานพื้นฐานของไฟล์ CSV คือโครงสร้างข้อมูลแบบสี่เหลี่ยมที่อ้างอิงจากชุดของส่วนหัวคอลัมน์เดียวที่ระบุในบรรทัดแรก สิ่งที่คุณดูเหมือนจะเป็นข้อมูลที่คั่นด้วยเครื่องหมายจุลภาคที่มีการแบ่งแยกโดยทั่วไปต้องการ "ETL" ที่ซับซ้อนมากขึ้นในการแยกวิเคราะห์จากไฟล์ไปยังอินสแตนซ์ของวัตถุที่มีประเภทแตกต่างกัน (ซึ่งอาจรวมถึง DataRows
KeithS

93

ฉันใช้OleDbผู้ให้บริการ อย่างไรก็ตามมันมีปัญหาหากคุณอ่านในแถวที่มีค่าตัวเลข แต่คุณต้องการให้มันเป็นข้อความ อย่างไรก็ตามคุณสามารถแก้ไขปัญหานั้นได้ด้วยการสร้างschema.iniไฟล์ นี่คือวิธีการของฉันที่ฉันใช้:

// using System.Data;
// using System.Data.OleDb;
// using System.Globalization;
// using System.IO;

static DataTable GetDataTableFromCsv(string path, bool isFirstRowHeader)
{
    string header = isFirstRowHeader ? "Yes" : "No";

    string pathOnly = Path.GetDirectoryName(path);
    string fileName = Path.GetFileName(path);

    string sql = @"SELECT * FROM [" + fileName + "]";

    using(OleDbConnection connection = new OleDbConnection(
              @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + pathOnly + 
              ";Extended Properties=\"Text;HDR=" + header + "\""))
    using(OleDbCommand command = new OleDbCommand(sql, connection))
    using(OleDbDataAdapter adapter = new OleDbDataAdapter(command))
    {
        DataTable dataTable = new DataTable();
        dataTable.Locale = CultureInfo.CurrentCulture;
        adapter.Fill(dataTable);
        return dataTable;
    }
}

ขอบคุณเพื่อน. นั่นช่วยฉัน ฉันมีไฟล์ CSV ซึ่งเครื่องหมายจุลภาคไม่ได้เป็นตัวคั่นเท่านั้นพวกเขาอยู่ทุกหนทุกแห่งภายในค่าคอลัมน์จำนวนมากดังนั้นการสร้าง regex ที่จะแยกบรรทัดนั้นค่อนข้างท้าทาย OleDbProvider อนุมานสกีมาอย่างถูกต้อง
Galilyou

การนำไปปฏิบัตินั้นสมเหตุสมผล แต่เราจะจัดการกับเซลล์ที่มีชนิดข้อมูลแบบผสมได้อย่างไร ตัวอย่างเช่น 40C และอื่น ๆ ?
GKED

GKED หากข้อมูลที่คุณกำลังอ่านมีชุดคอลัมน์และประเภทที่คุณสามารถวางไว้ในโฟลเดอร์เดียวกันไฟล์ shema.ini ที่บอกข้อมูลผู้ให้บริการ OleDb เกี่ยวกับคอลัมน์นั้น นี่คือลิงค์ไปยังบทความของ Microsoft ที่ให้รายละเอียดเกี่ยวกับวิธีจัดโครงสร้างไฟล์ msdn.microsoft.com/en-us/library/…
Jim Scott

4
ในขณะที่คำตอบนี้จะได้ผลฉันขอแนะนำอย่างยิ่งยวด คุณแนะนำการพึ่งพาภายนอกซึ่งอาจขัดแย้งกับการติดตั้งสำนักงานอื่น ๆ บนเครื่องเดียวกัน (ใช้ Excel ในสภาพแวดล้อม dev ท้องถิ่นของคุณหรือไม่) ขึ้นอยู่กับรุ่นที่ติดตั้ง มีแพ็กเกจ NuGet อยู่ที่นั่น (ExcelDataReader, CsvHelper) ซึ่งทำสิ่งนี้ได้อย่างมีประสิทธิภาพและพกพาได้มากกว่า
A. Murray

1
@ A.Murray - คุณหมายถึงอะไรกันแน่? สิ่งนี้ใช้ตัวให้บริการ OleDb ในตัวใน System.Data.dll คุณไม่จำเป็นต้องติดตั้ง "ไดรเวอร์" เพิ่มเติมใด ๆ และฉันจะตกใจในวันนี้และอายุถ้าการติดตั้ง windows ไม่มีการติดตั้งไดรเวอร์ Jet พื้นฐาน นี่คือ CSV ของปี 1990 ....
Paul Easter

40

ฉันได้ตัดสินใจที่จะใช้เซบาสเตียน Lorion ของ Csv อ่าน

ข้อเสนอแนะของ Jay Riggs ก็เป็นทางเลือกที่ยอดเยี่ยมเช่นกัน แต่ฉันไม่ต้องการคุณสมบัติทั้งหมดที่Generic Parser ของ Andrew Rissingมอบให้

อัพเดท 10/25/2010

หลังจากใช้Csv ReaderของSebastien Lorionในโครงการของฉันเกือบหนึ่งปีครึ่งฉันพบว่ามันมีข้อยกเว้นเมื่อทำการวิเคราะห์ไฟล์ csv บางไฟล์ที่ฉันเชื่อว่ามีรูปแบบที่ดี

ดังนั้นฉันเปลี่ยนไปใช้Generic Parser ของ Andrew Rissingและดูเหมือนว่าจะทำได้ดีขึ้นมาก

อัพเดท 9/22/2014

วันนี้ฉันส่วนใหญ่ใช้วิธีการขยายนี้เพื่ออ่านข้อความที่คั่นด้วย:

https://github.com/Core-Techs/Common/blob/master/CoreTechs.Common/Text/DelimitedTextExtensions.cs#L22

https://www.nuget.org/packages/CoreTechs.Common/

อัปเดต 2/25/2015

ตัวอย่าง:

var csv = @"Name, Age
Ronnie, 30
Mark, 40
Ace, 50";

TextReader reader = new StringReader(csv);
var table = new DataTable();
using(var it = reader.ReadCsvWithHeader().GetEnumerator())
{

    if (!it.MoveNext()) return;

    foreach (var k in it.Current.Keys)
        table.Columns.Add(k);

    do
    {
        var row = table.NewRow();
        foreach (var k in it.Current.Keys)
            row[k] = it.Current[k];
    
        table.Rows.Add(row);
    
    } while (it.MoveNext());
}

ฉันยอมรับว่าผู้อ่าน CSV ของ Sebastien Lorien นั้นยอดเยี่ยม ฉันใช้มันสำหรับการประมวลผล CSV จำนวนมาก แต่ฉันก็ใช้ Rissing ของ Andrew สำหรับงานเล็ก ๆ และมันก็ใช้ได้ดี มีความสุข!
Jay Riggs

ฉันจะใช้คลาสเหล่านี้เพื่อโหลด CSV ลงใน DATATABLE ได้อย่างไร
Muflix

ฉันลองสิ่งนี้ แต่คอลเลคชั่น it.Cey.Keys กลับมาพร้อมกับ "System.Linq.Enumerable + WhereSelectListIterator`2 [System.Int32, System.Char]" แทนที่จะเป็นชื่อของคอลัมน์ ความคิดใด ๆ ว่าเป็นเพราะเหตุใด
user3658298

คุณสามารถใช้ตัวคั่นหลายตัวได้หรือไม่?
ม้วน

ไม่ แต่ฉันคิดเกี่ยวกับการเปิดใช้งาน
Ronnie Overby

32

เฮ้มันใช้งานได้100%

  public static DataTable ConvertCSVtoDataTable(string strFilePath)
  {
    DataTable dt = new DataTable();
    using (StreamReader sr = new StreamReader(strFilePath))
    {
        string[] headers = sr.ReadLine().Split(',');
        foreach (string header in headers)
        {
            dt.Columns.Add(header);
        }
        while (!sr.EndOfStream)
        {
            string[] rows = sr.ReadLine().Split(',');
            DataRow dr = dt.NewRow();
            for (int i = 0; i < headers.Length; i++)
            {
                dr[i] = rows[i];
            }
            dt.Rows.Add(dr);
        }

    }


    return dt;
   }

รูปภาพ CSV ป้อนคำอธิบายรูปภาพที่นี่

นำเข้าตารางข้อมูล ป้อนคำอธิบายรูปภาพที่นี่


7
เฉพาะเมื่ออินพุต 100% เป็นไฟล์ CSV ที่ง่ายที่สุด (ซึ่งอาจเป็นจริงในกรณีของคุณ)
Ronnie Overby

คุณถูก. คุณควรใช้codeproject.com/Articles/9258/A-Fast-CSV-Reader (Lorion dll) ฉันพยายามใช้งานได้ดี
Shivam Srivastava

1
ดูคำตอบของฉันจาก 2009
Ronnie Overby

1
@ShivamSrivastava ฉันได้รับข้อผิดพลาดในแถวสุดท้ายแล้วคุณให้ข้อมูลการติดต่ออื่น ๆ แก่คุณ
Sunil Acharya

แม้ว่าฉันจะไม่ได้ใช้เวอร์ชันนี้อย่างแน่นอน แต่ก็เป็นไปตามที่ฉันแก้ไขปัญหาของฉัน ขอบคุณ. ทำงานได้ดีมาก
nrod

13

เรามักจะใช้ไดรเวอร์ Jet.OLEDB จนกว่าเราจะเริ่มใช้งานแอพพลิเคชั่น 64 บิต Microsoft ไม่ได้และจะไม่ปล่อยไดร์เวอร์ Jet 64 บิต นี่เป็นวิธีง่ายๆที่เราใช้กับ File.ReadAllLines และ String.Split เพื่ออ่านและแยกไฟล์ CSV และโหลด DataTable ด้วยตนเอง ดังที่ระบุไว้ข้างต้นไม่สามารถจัดการกับสถานการณ์ที่หนึ่งในค่าคอลัมน์มีเครื่องหมายจุลภาค เราใช้สิ่งนี้เป็นส่วนใหญ่ในการอ่านไฟล์กำหนดค่าส่วนที่ดีเกี่ยวกับการใช้ไฟล์ CSV คือเราสามารถแก้ไขได้ใน Excel

string CSVFilePathName = @"C:\test.csv";
string[] Lines = File.ReadAllLines(CSVFilePathName);
string[] Fields;
Fields = Lines[0].Split(new char[] { ',' });
int Cols = Fields.GetLength(0);
DataTable dt = new DataTable();
//1st row must be column names; force lower case to ensure matching later on.
for (int i = 0; i < Cols; i++)
    dt.Columns.Add(Fields[i].ToLower(), typeof(string));
DataRow Row;
for (int i = 1; i < Lines.GetLength(0); i++)
{
    Fields = Lines[i].Split(new char[] { ',' });
    Row = dt.NewRow();
    for (int f = 0; f < Cols; f++)
        Row[f] = Fields[f];
    dt.Rows.Add(Row);
}

8

นี่คือรหัสที่ฉันใช้ แต่แอปของคุณต้องทำงานด้วย net เวอร์ชั่น 3.5

private void txtRead_Click(object sender, EventArgs e)
        {
           // var filename = @"d:\shiptest.txt";

            openFileDialog1.InitialDirectory = "d:\\";
            openFileDialog1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
            DialogResult result = openFileDialog1.ShowDialog();
            if (result == DialogResult.OK)
            {
                if (openFileDialog1.FileName != "")
                {
                    var reader = ReadAsLines(openFileDialog1.FileName);

                    var data = new DataTable();

                    //this assume the first record is filled with the column names
                    var headers = reader.First().Split(',');
                    foreach (var header in headers)
                    {
                        data.Columns.Add(header);
                    }

                    var records = reader.Skip(1);
                    foreach (var record in records)
                    {
                        data.Rows.Add(record.Split(','));
                    }

                    dgList.DataSource = data;
                }
            }
        }

        static IEnumerable<string> ReadAsLines(string filename)
        {
            using (StreamReader reader = new StreamReader(filename))
                while (!reader.EndOfStream)
                    yield return reader.ReadLine();
        }

นี่คือสิ่งที่ฉันต้องการนำเสนอ
กัปตัน Kenpachi

8

คุณสามารถทำได้โดยใช้ Microsoft.VisualBasic.FileIO.TextFieldParser dll ใน C #

static void Main()
        {
            string csv_file_path=@"C:\Users\Administrator\Desktop\test.csv";

            DataTable csvData = GetDataTabletFromCSVFile(csv_file_path);

            Console.WriteLine("Rows count:" + csvData.Rows.Count);

            Console.ReadLine();
        }


private static DataTable GetDataTabletFromCSVFile(string csv_file_path)
        {
            DataTable csvData = new DataTable();

            try
            {

            using(TextFieldParser csvReader = new TextFieldParser(csv_file_path))
                {
                    csvReader.SetDelimiters(new string[] { "," });
                    csvReader.HasFieldsEnclosedInQuotes = true;
                    string[] colFields = csvReader.ReadFields();
                    foreach (string column in colFields)
                    {
                        DataColumn datecolumn = new DataColumn(column);
                        datecolumn.AllowDBNull = true;
                        csvData.Columns.Add(datecolumn);
                    }

                    while (!csvReader.EndOfData)
                    {
                        string[] fieldData = csvReader.ReadFields();
                        //Making empty value as null
                        for (int i = 0; i < fieldData.Length; i++)
                        {
                            if (fieldData[i] == "")
                            {
                                fieldData[i] = null;
                            }
                        }
                        csvData.Rows.Add(fieldData);
                    }
                }
            }
            catch (Exception ex)
            {
            }
            return csvData;
        }

โปรดอย่าพยายามคิดค้นวงล้อใหม่ด้วยการประมวลผล CSV มีทางเลือกโอเพนซอร์ซมากมายให้เลือกมากมาย
Mike Cole

1
ขอบคุณแบรดเคล็ดลับที่เป็นประโยชน์เกี่ยวกับ TextFieldParser สำหรับการจัดการคำพูดฝังตัว
mattpm

3
public class Csv
{
    public static DataTable DataSetGet(string filename, string separatorChar, out List<string> errors)
    {
        errors = new List<string>();
        var table = new DataTable("StringLocalization");
        using (var sr = new StreamReader(filename, Encoding.Default))
        {
            string line;
            var i = 0;
            while (sr.Peek() >= 0)
            {
                try
                {
                    line = sr.ReadLine();
                    if (string.IsNullOrEmpty(line)) continue;
                    var values = line.Split(new[] {separatorChar}, StringSplitOptions.None);
                    var row = table.NewRow();
                    for (var colNum = 0; colNum < values.Length; colNum++)
                    {
                        var value = values[colNum];
                        if (i == 0)
                        {
                            table.Columns.Add(value, typeof (String));
                        }
                        else
                        {
                            row[table.Columns[colNum]] = value;
                        }
                    }
                    if (i != 0) table.Rows.Add(row);
                }
                catch(Exception ex)
                {
                    errors.Add(ex.Message);
                }
                i++;
            }
        }
        return table;
    }
}

3

ฉันเจอโค้ดนี้ที่ใช้ Linq และ regex เพื่อแยกไฟล์ CSV บทความที่อ้างอิงตอนนี้เป็นเวลาหนึ่งปีครึ่งแล้ว แต่ยังไม่พบวิธีที่จะแยกวิเคราะห์ CSV โดยใช้ Linq (และ regex) ได้มากกว่านี้ ข้อแม้คือ regex ที่ใช้ที่นี่มีไว้สำหรับไฟล์ที่คั่นด้วยเครื่องหมายจุลภาค (จะตรวจจับเครื่องหมายจุลภาคในเครื่องหมายคำพูด!) และอาจไม่ดีสำหรับส่วนหัว แต่มีวิธีที่จะเอาชนะสิ่งเหล่านี้) จุดสูงสุด:

Dim lines As String() = System.IO.File.ReadAllLines(strCustomerFile)
Dim pattern As String = ",(?=(?:[^""]*""[^""]*"")*(?![^""]*""))"
Dim r As System.Text.RegularExpressions.Regex = New System.Text.RegularExpressions.Regex(pattern)
Dim custs = From line In lines _
            Let data = r.Split(line) _
                Select New With {.custnmbr = data(0), _
                                 .custname = data(1)}
For Each cust In custs
    strCUSTNMBR = Replace(cust.custnmbr, Chr(34), "")
    strCUSTNAME = Replace(cust.custname, Chr(34), "")
Next

3

ตัวเลือกที่ดีที่สุดที่ฉันได้พบและช่วยแก้ปัญหาที่คุณอาจจะมีรุ่นที่แตกต่างของการติดตั้ง Office และ 32 ประเด็น / 64-bit เช่นโยน Bevitt กล่าวถึงเป็นFileHelpers

สามารถเพิ่มลงในการอ้างอิงโครงการของคุณโดยใช้ NuGet และให้บริการโซลูชั่นแบบหนึ่งซับ:

CommonEngine.CsvToDataTable(path, "ImportRecord", ',', true);

คุณบอกได้ไหมว่า CommonEngine คืออะไร NuGet เหมือนกับ NuGet หรือไม่ ฉันพบเฉพาะ NuGet.Core ในการอ้างอิง
sindhu jampani

มันเป็น FileHelpers ที่คุณต้องการ หากคุณมี NuGet ให้เพิ่มด้วย NuGet มิฉะนั้นเพียงเพิ่มเป็นชุดประกอบในโครงการของคุณ CommonEngine เป็นส่วนหนึ่งของ FileHelpers
Neo

3

สำหรับผู้ที่คุณไม่ต้องการใช้ห้องสมุดภายนอกและไม่ต้องการใช้ OleDB โปรดดูตัวอย่างด้านล่าง ทุกสิ่งที่ฉันพบคือ OleDB ห้องสมุดภายนอกหรือแยกตามคอมม่า! สำหรับกรณีของฉัน OleDB ไม่ทำงานดังนั้นฉันต้องการสิ่งที่แตกต่าง

ผมพบว่าบทความโดย MarkJ ที่อ้างอิงวิธี Microsoft.VisualBasic.FileIO.TextFieldParser เป็นเห็นที่นี่ บทความนี้เขียนขึ้นใน VB และไม่ส่งคืน DataTable ดังนั้นให้ดูตัวอย่างของฉันด้านล่าง

public static DataTable LoadCSV(string path, bool hasHeader)
    {
        DataTable dt = new DataTable();

        using (var MyReader = new Microsoft.VisualBasic.FileIO.TextFieldParser(path))
        {
            MyReader.TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited;
            MyReader.Delimiters = new String[] { "," };

            string[] currentRow;

            //'Loop through all of the fields in the file.  
            //'If any lines are corrupt, report an error and continue parsing.  
            bool firstRow = true;
            while (!MyReader.EndOfData)
            {
                try
                {
                    currentRow = MyReader.ReadFields();

                    //Add the header columns
                    if (hasHeader && firstRow)
                    {
                        foreach (string c in currentRow)
                        {
                            dt.Columns.Add(c, typeof(string));
                        }

                        firstRow = false;
                        continue;
                    }

                    //Create a new row
                    DataRow dr = dt.NewRow();
                    dt.Rows.Add(dr);

                    //Loop thru the current line and fill the data out
                    for(int c = 0; c < currentRow.Count(); c++)
                    {
                        dr[c] = currentRow[c];
                    }
                }
                catch (Microsoft.VisualBasic.FileIO.MalformedLineException ex)
                {
                    //Handle the exception here
                }
            }
        }

        return dt;
    }

3

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

 private DataTable csvToDataTable(string fileName, char splitCharacter)
    {                
        StreamReader sr = new StreamReader(fileName);
        string myStringRow = sr.ReadLine();
        var rows = myStringRow.Split(splitCharacter);
        DataTable CsvData = new DataTable();
        foreach (string column in rows)
        {
            //creates the columns of new datatable based on first row of csv
            CsvData.Columns.Add(column);
        }
        myStringRow = sr.ReadLine();
        while (myStringRow != null)
        {
            //runs until string reader returns null and adds rows to dt 
            rows = myStringRow.Split(splitCharacter);
            CsvData.Rows.Add(rows);
            myStringRow = sr.ReadLine();
        }
        sr.Close();
        sr.Dispose();
        return CsvData;
    }

วิธีการของฉันถ้าฉันกำลังนำเข้าตารางที่มีสตริง [] ตัวคั่นและจัดการปัญหาที่บรรทัดปัจจุบันที่ฉันกำลังอ่านอาจไปที่บรรทัดถัดไปใน csv หรือไฟล์ข้อความ <- ในกรณีที่ฉันต้องการวนซ้ำจนกว่าฉันจะได้รับ ถึงจำนวนบรรทัดทั้งหมดในแถวแรก (คอลัมน์)

public static DataTable ImportCSV(string fullPath, string[] sepString)
    {
        DataTable dt = new DataTable();
        using (StreamReader sr = new StreamReader(fullPath))
        {
           //stream uses using statement because it implements iDisposable
            string firstLine = sr.ReadLine();
            var headers = firstLine.Split(sepString, StringSplitOptions.None);
            foreach (var header in headers)
            {
               //create column headers
                dt.Columns.Add(header);
            }
            int columnInterval = headers.Count();
            string newLine = sr.ReadLine();
            while (newLine != null)
            {
                //loop adds each row to the datatable
                var fields = newLine.Split(sepString, StringSplitOptions.None); // csv delimiter    
                var currentLength = fields.Count();
                if (currentLength < columnInterval)
                {
                    while (currentLength < columnInterval)
                    {
                       //if the count of items in the row is less than the column row go to next line until count matches column number total
                        newLine += sr.ReadLine();
                        currentLength = newLine.Split(sepString, StringSplitOptions.None).Count();
                    }
                    fields = newLine.Split(sepString, StringSplitOptions.None);
                }
                if (currentLength > columnInterval)
                {  
                    //ideally never executes - but if csv row has too many separators, line is skipped
                    newLine = sr.ReadLine();
                    continue;
                }
                dt.Rows.Add(fields);
                newLine = sr.ReadLine();
            }
            sr.Close();
        }

        return dt;
    }

คุณก็ยังไม่ได้ประกาศแถวเป็นสตริง []
สไตล์สัตว์

@AnimalStyle คุณถูกต้อง - อัปเดตด้วยวิธีที่มีประสิทธิภาพยิ่งขึ้นและประกาศแถว
Matt Farguson

3

ดัดแปลงจาก Mr ChuckBevitt

วิธีการทำงาน:

string CSVFilePathName = APP_PATH + "Facilities.csv";
string[] Lines = File.ReadAllLines(CSVFilePathName);
string[] Fields;
Fields = Lines[0].Split(new char[] { ',' });
int Cols = Fields.GetLength(0);
DataTable dt = new DataTable();
//1st row must be column names; force lower case to ensure matching later on.
for (int i = 0; i < Cols-1; i++)
        dt.Columns.Add(Fields[i].ToLower(), typeof(string));
DataRow Row;
for (int i = 0; i < Lines.GetLength(0)-1; i++)
{
        Fields = Lines[i].Split(new char[] { ',' });
        Row = dt.NewRow();
        for (int f = 0; f < Cols-1; f++)
                Row[f] = Fields[f];
        dt.Rows.Add(Row);
}

ดังนั้นนี่จะแก้ปัญหาเรื่องความจำใช่มั้ย นี่คือการประมวลผลตามสายและไม่คงอยู่ในหน่วยความจำดังนั้นจึงไม่มีข้อยกเว้น? ฉันชอบวิธีการประมวลผลนี้ แต่ไม่มี File.ReadAllLines () บันทึกลงในหน่วยความจำทั้งหมดหรือไม่ ฉันคิดว่าคุณควรใช้ File.ReadLines () เพื่อหลีกเลี่ยงบัฟเฟอร์หน่วยความจำขนาดใหญ่? นี่เป็นคำตอบที่ดีสำหรับคำถามที่ฉันต้องการทราบเกี่ยวกับความจำ
DtechNet

2

นี่คือโซลูชันที่ใช้โปรแกรมควบคุมข้อความ ODBC ของ ADO.Net:

Dim csvFileFolder As String = "C:\YourFileFolder"
Dim csvFileName As String = "YourFile.csv"

'Note that the folder is specified in the connection string,
'not the file. That's specified in the SELECT query, later.
Dim connString As String = "Driver={Microsoft Text Driver (*.txt; *.csv)};Dbq=" _
    & csvFileFolder & ";Extended Properties=""Text;HDR=No;FMT=Delimited"""
Dim conn As New Odbc.OdbcConnection(connString)

'Open a data adapter, specifying the file name to load
Dim da As New Odbc.OdbcDataAdapter("SELECT * FROM [" & csvFileName & "]", conn)
'Then fill a data table, which can be bound to a grid
Dim dt As New DataTableda.Fill(dt)

grdCSVData.DataSource = dt

เมื่อเติมแล้วคุณสามารถให้ความสำคัญกับคุณสมบัติของ DataTable เช่น ColumnName เพื่อใช้ประโยชน์จากพลังทั้งหมดของวัตถุข้อมูล ADO.Net

ใน VS2008 คุณสามารถใช้ Linq เพื่อให้ได้ผลเช่นเดียวกัน

หมายเหตุ: นี่อาจจะเป็นซ้ำนี้คำถาม SO


2

ไม่สามารถต้านทานการเพิ่มสปินของฉันเองในสิ่งนี้ มันดีกว่าและกะทัดรัดกว่าที่ฉันเคยใช้ในอดีตมาก

วิธีนี้:

  • ไม่ขึ้นอยู่กับไดรเวอร์ฐานข้อมูลหรือไลบรารีบุคคลที่สาม
  • จะไม่ล้มเหลวในชื่อคอลัมน์ที่ซ้ำกัน
  • จัดการเครื่องหมายจุลภาคในข้อมูล
  • จัดการตัวคั่นใด ๆ ไม่ใช่แค่เครื่องหมายจุลภาค (แม้ว่าจะเป็นค่าเริ่มต้น)

นี่คือสิ่งที่ฉันมาด้วย:

  Public Function ToDataTable(FileName As String, Optional Delimiter As String = ",") As DataTable
    ToDataTable = New DataTable
    Using TextFieldParser As New Microsoft.VisualBasic.FileIO.TextFieldParser(FileName) With
      {.HasFieldsEnclosedInQuotes = True, .TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited, .TrimWhiteSpace = True}
      With TextFieldParser
        .SetDelimiters({Delimiter})
        .ReadFields.ToList.Unique.ForEach(Sub(x) ToDataTable.Columns.Add(x))
        ToDataTable.Columns.Cast(Of DataColumn).ToList.ForEach(Sub(x) x.AllowDBNull = True)
        Do Until .EndOfData
          ToDataTable.Rows.Add(.ReadFields.Select(Function(x) Text.BlankToNothing(x)).ToArray)
        Loop
      End With
    End Using
  End Function

มันขึ้นอยู่กับวิธีการขยาย ( Unique) เพื่อจัดการชื่อคอลัมน์ที่ซ้ำกันที่จะพบว่าเป็นคำตอบของฉันในวิธีการผนวกหมายเลขที่ไม่ซ้ำกันในรายการของสตริง

และนี่คือBlankToNothingฟังก์ชันผู้ช่วย:

  Public Function BlankToNothing(ByVal Value As String) As Object 
    If String.IsNullOrEmpty(Value) Then Return Nothing
    Return Value
  End Function

2

ด้วยCinchoo ETL - ไลบรารีโอเพ่นซอร์สคุณสามารถแปลงไฟล์ CSV เป็น DataTable ด้วยโค้ดไม่กี่บรรทัด

using (var p = new ChoCSVReader(** YOUR CSV FILE **)
     .WithFirstLineHeader()
    )
{
    var dt = p.AsDataTable();
}

สำหรับข้อมูลเพิ่มเติมกรุณาเยี่ยมชมบทความcodeproject

หวังว่ามันจะช่วย


2
    private static DataTable LoadCsvData(string refPath)
    {
        var cfg = new Configuration() { Delimiter = ",", HasHeaderRecord = true };
        var result = new DataTable();
        using (var sr = new StreamReader(refPath, Encoding.UTF8, false, 16384 * 2))
        {
            using (var rdr = new CsvReader(sr, cfg))
            using (var dataRdr = new CsvDataReader(rdr))
            {
                result.Load(dataRdr);
            }
        }
        return result;
    }

ใช้: https://joshclose.github.io/CsvHelper/


โปรดทราบว่าในรีลีส 13 Configurationถูกเปลี่ยนชื่อเป็นCsvConfiguration เพื่อหลีกเลี่ยงความขัดแย้งของเนมสเปซ การสาธิตการทำงานของคำตอบนี้: dotnetfiddle.net/sdwc6i
DBC

2

ฉันใช้ไลบรารีชื่อ ExcelDataReader คุณสามารถค้นหาได้บน NuGet ตรวจสอบให้แน่ใจว่าได้ติดตั้งทั้ง ExcelDataReader และ ExcelDataReader.DataSet extension (ส่วนหลังแสดงวิธี AsDataSet ที่จำเป็นซึ่งอ้างถึงด้านล่าง)

ฉันห่อหุ้มทุกอย่างในฟังก์ชันเดียวคุณสามารถคัดลอกมันในรหัสของคุณโดยตรง กำหนดเส้นทางไปยังไฟล์ CSV มันจะทำให้คุณได้รับชุดข้อมูลที่มีหนึ่งตาราง

public static DataSet GetDataSet(string filepath)
{
   var stream = File.OpenRead(filepath);

   try
   {
       var reader = ExcelReaderFactory.CreateCsvReader(stream, new ExcelReaderConfiguration()
       {
           LeaveOpen = false
       });

       var result = reader.AsDataSet(new ExcelDataSetConfiguration()
       {
           // Gets or sets a value indicating whether to set the DataColumn.DataType 
           // property in a second pass.
           UseColumnDataType = true,

           // Gets or sets a callback to determine whether to include the current sheet
           // in the DataSet. Called once per sheet before ConfigureDataTable.
           FilterSheet = (tableReader, sheetIndex) => true,

           // Gets or sets a callback to obtain configuration options for a DataTable. 
           ConfigureDataTable = (tableReader) => new ExcelDataTableConfiguration()
           {
               // Gets or sets a value indicating the prefix of generated column names.
               EmptyColumnNamePrefix = "Column",

               // Gets or sets a value indicating whether to use a row from the 
               // data as column names.
               UseHeaderRow = true,

               // Gets or sets a callback to determine which row is the header row. 
               // Only called when UseHeaderRow = true.
               ReadHeaderRow = (rowReader) =>
               {
                   // F.ex skip the first row and use the 2nd row as column headers:
                   //rowReader.Read();
               },

               // Gets or sets a callback to determine whether to include the 
               // current row in the DataTable.
               FilterRow = (rowReader) =>
               {
                   return true;
               },

               // Gets or sets a callback to determine whether to include the specific
               // column in the DataTable. Called once per column after reading the 
               // headers.
               FilterColumn = (rowReader, columnIndex) =>
               {
                   return true;
               }
           }
       });

       return result;
   }
   catch (Exception ex)
   {
       return null;
   }
   finally
   {
       stream.Close();
       stream.Dispose();
   }
}

มันคือปี 2020 และนี่เป็นทางออกที่ยอดเยี่ยมเมื่อเทียบกับคำตอบเก่า ๆ ที่นี่ มันบรรจุไว้อย่างดีและใช้ห้องสมุดยอดนิยมและน้ำหนักเบาจาก NuGet และมีความยืดหยุ่น - หาก CSV ของคุณอยู่ในหน่วยความจำเพียงแค่ส่งเป็นMemoryStreamแทนเส้นทางไฟล์ DataTable ที่ OP ขอแยกออกได้ง่ายจาก DataSet ดังนี้:result.Tables[0]
Tawab Wakil

1

เพียงแชร์วิธีการส่วนขยายนี้ฉันหวังว่าจะสามารถช่วยคนอื่นได้

public static List<string> ToCSV(this DataSet ds, char separator = '|')
{
    List<string> lResult = new List<string>();

    foreach (DataTable dt in ds.Tables)
    {
        StringBuilder sb = new StringBuilder();
        IEnumerable<string> columnNames = dt.Columns.Cast<DataColumn>().
                                          Select(column => column.ColumnName);
        sb.AppendLine(string.Join(separator.ToString(), columnNames));

        foreach (DataRow row in dt.Rows)
        {
            IEnumerable<string> fields = row.ItemArray.Select(field =>
              string.Concat("\"", field.ToString().Replace("\"", "\"\""), "\""));
            sb.AppendLine(string.Join(separator.ToString(), fields));
        }

        lResult.Add(sb.ToString());
    }
    return lResult;
}

public static DataSet CSVtoDataSet(this List<string> collectionCSV, char separator = '|')
{
    var ds = new DataSet();

    foreach (var csv in collectionCSV)
    {
        var dt = new DataTable();

        var readHeader = false;
        foreach (var line in csv.Split(new[] { Environment.NewLine }, StringSplitOptions.None))
        {
            if (!readHeader)
            {
                foreach (var c in line.Split(separator))
                    dt.Columns.Add(c);
            }
            else
            {
                dt.Rows.Add(line.Split(separator));
            }
        }

        ds.Tables.Add(dt);
    }

    return ds;
}

0

ใช้สิ่งนี้หนึ่งฟังก์ชั่นแก้ปัญหาทั้งหมดของเครื่องหมายจุลภาคและพูด:

public static DataTable CsvToDataTable(string strFilePath)
    {

        if (File.Exists(strFilePath))
        {

            string[] Lines;
            string CSVFilePathName = strFilePath;

            Lines = File.ReadAllLines(CSVFilePathName);
            while (Lines[0].EndsWith(","))
            {
                Lines[0] = Lines[0].Remove(Lines[0].Length - 1);
            }
            string[] Fields;
            Fields = Lines[0].Split(new char[] { ',' });
            int Cols = Fields.GetLength(0);
            DataTable dt = new DataTable();
            //1st row must be column names; force lower case to ensure matching later on.
            for (int i = 0; i < Cols; i++)
                dt.Columns.Add(Fields[i], typeof(string));
            DataRow Row;
            int rowcount = 0;
            try
            {
                string[] ToBeContinued = new string[]{};
                bool lineToBeContinued = false;
                for (int i = 1; i < Lines.GetLength(0); i++)
                {
                    if (!Lines[i].Equals(""))
                    {
                        Fields = Lines[i].Split(new char[] { ',' });
                        string temp0 = string.Join("", Fields).Replace("\"\"", "");
                        int quaotCount0 = temp0.Count(c => c == '"');
                        if (Fields.GetLength(0) < Cols || lineToBeContinued || quaotCount0 % 2 != 0)
                        {
                            if (ToBeContinued.GetLength(0) > 0)
                            {
                                ToBeContinued[ToBeContinued.Length - 1] += "\n" + Fields[0];
                                Fields = Fields.Skip(1).ToArray();
                            }
                            string[] newArray = new string[ToBeContinued.Length + Fields.Length];
                            Array.Copy(ToBeContinued, newArray, ToBeContinued.Length);
                            Array.Copy(Fields, 0, newArray, ToBeContinued.Length, Fields.Length);
                            ToBeContinued = newArray;
                            string temp = string.Join("", ToBeContinued).Replace("\"\"", "");
                            int quaotCount = temp.Count(c => c == '"');
                            if (ToBeContinued.GetLength(0) >= Cols && quaotCount % 2 == 0 )
                            {
                                Fields = ToBeContinued;
                                ToBeContinued = new string[] { };
                                lineToBeContinued = false;
                            }
                            else
                            {
                                lineToBeContinued = true;
                                continue;
                            }
                        }

                        //modified by Teemo @2016 09 13
                        //handle ',' and '"'
                        //Deserialize CSV following Excel's rule:
                        // 1: If there is commas in a field, quote the field.
                        // 2: Two consecutive quotes indicate a user's quote.

                        List<int> singleLeftquota = new List<int>();
                        List<int> singleRightquota = new List<int>();

                        //combine fileds if number of commas match
                        if (Fields.GetLength(0) > Cols) 
                        {
                            bool lastSingleQuoteIsLeft = true;
                            for (int j = 0; j < Fields.GetLength(0); j++)
                            {
                                bool leftOddquota = false;
                                bool rightOddquota = false;
                                if (Fields[j].StartsWith("\"")) 
                                {
                                    int numberOfConsecutiveQuotes = 0;
                                    foreach (char c in Fields[j]) //start with how many "
                                    {
                                        if (c == '"')
                                        {
                                            numberOfConsecutiveQuotes++;
                                        }
                                        else
                                        {
                                            break;
                                        }
                                    }
                                    if (numberOfConsecutiveQuotes % 2 == 1)//start with odd number of quotes indicate system quote
                                    {
                                        leftOddquota = true;
                                    }
                                }

                                if (Fields[j].EndsWith("\""))
                                {
                                    int numberOfConsecutiveQuotes = 0;
                                    for (int jj = Fields[j].Length - 1; jj >= 0; jj--)
                                    {
                                        if (Fields[j].Substring(jj,1) == "\"") // end with how many "
                                        {
                                            numberOfConsecutiveQuotes++;
                                        }
                                        else
                                        {
                                            break;
                                        }
                                    }

                                    if (numberOfConsecutiveQuotes % 2 == 1)//end with odd number of quotes indicate system quote
                                    {
                                        rightOddquota = true;
                                    }
                                }
                                if (leftOddquota && !rightOddquota)
                                {
                                    singleLeftquota.Add(j);
                                    lastSingleQuoteIsLeft = true;
                                }
                                else if (!leftOddquota && rightOddquota)
                                {
                                    singleRightquota.Add(j);
                                    lastSingleQuoteIsLeft = false;
                                }
                                else if (Fields[j] == "\"") //only one quota in a field
                                {
                                    if (lastSingleQuoteIsLeft)
                                    {
                                        singleRightquota.Add(j);
                                    }
                                    else
                                    {
                                        singleLeftquota.Add(j);
                                    }
                                }
                            }
                            if (singleLeftquota.Count == singleRightquota.Count)
                            {
                                int insideCommas = 0;
                                for (int indexN = 0; indexN < singleLeftquota.Count; indexN++)
                                {
                                    insideCommas += singleRightquota[indexN] - singleLeftquota[indexN];
                                }
                                if (Fields.GetLength(0) - Cols >= insideCommas) //probabaly matched
                                {
                                    int validFildsCount = insideCommas + Cols; //(Fields.GetLength(0) - insideCommas) may be exceed the Cols
                                    String[] temp = new String[validFildsCount];
                                    int totalOffSet = 0;
                                    for (int iii = 0; iii < validFildsCount - totalOffSet; iii++)
                                    {
                                        bool combine = false;
                                        int storedIndex = 0;
                                        for (int iInLeft = 0; iInLeft < singleLeftquota.Count; iInLeft++)
                                        {
                                            if (iii + totalOffSet == singleLeftquota[iInLeft])
                                            {
                                                combine = true;
                                                storedIndex = iInLeft;
                                                break;
                                            }
                                        }
                                        if (combine)
                                        {
                                            int offset = singleRightquota[storedIndex] - singleLeftquota[storedIndex];
                                            for (int combineI = 0; combineI <= offset; combineI++)
                                            {
                                                temp[iii] += Fields[iii + totalOffSet + combineI] + ",";
                                            }
                                            temp[iii] = temp[iii].Remove(temp[iii].Length - 1, 1);
                                            totalOffSet += offset;
                                        }
                                        else
                                        {
                                            temp[iii] = Fields[iii + totalOffSet];
                                        }
                                    }
                                    Fields = temp;
                                }
                            }
                        }
                        Row = dt.NewRow();
                        for (int f = 0; f < Cols; f++)
                        {
                            Fields[f] = Fields[f].Replace("\"\"", "\""); //Two consecutive quotes indicate a user's quote
                            if (Fields[f].StartsWith("\""))
                            {
                                if (Fields[f].EndsWith("\""))
                                {
                                    Fields[f] = Fields[f].Remove(0, 1);
                                    if (Fields[f].Length > 0)
                                    {
                                        Fields[f] = Fields[f].Remove(Fields[f].Length - 1, 1);
                                    }
                                }
                            }
                            Row[f] = Fields[f];
                        }
                        dt.Rows.Add(Row);
                        rowcount++;
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception( "row: " + (rowcount+2) + ", " + ex.Message);
            }
            //OleDbConnection connection = new OleDbConnection(string.Format(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0}; Extended Properties=""text;HDR=Yes;FMT=Delimited"";", FilePath + FileName));
            //OleDbCommand command = new OleDbCommand("SELECT * FROM " + FileName, connection);
            //OleDbDataAdapter adapter = new OleDbDataAdapter(command);
            //DataTable dt = new DataTable();
            //adapter.Fill(dt);
            //adapter.Dispose();
            return dt;
        }
        else
            return null;

        //OleDbConnection connection = new OleDbConnection(string.Format(@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0}; Extended Properties=""text;HDR=Yes;FMT=Delimited"";", strFilePath));
        //OleDbCommand command = new OleDbCommand("SELECT * FROM " + strFileName, connection);
        //OleDbDataAdapter adapter = new OleDbDataAdapter(command);
        //DataTable dt = new DataTable();
        //adapter.Fill(dt);
        //return dt;
    }

0
 Public Function ReadCsvFileToDataTable(strFilePath As String) As DataTable
    Dim dtCsv As DataTable = New DataTable()
    Dim Fulltext As String
    Using sr As StreamReader = New StreamReader(strFilePath)
        While Not sr.EndOfStream
            Fulltext = sr.ReadToEnd().ToString()
            Dim rows As String() = Fulltext.Split(vbLf)
            For i As Integer = 0 To rows.Count() - 1 - 1
                Dim rowValues As String() = rows(i).Split(","c)
                If True Then
                    If i = 0 Then
                        For j As Integer = 0 To rowValues.Count() - 1
                            dtCsv.Columns.Add(rowValues(j))
                        Next
                    Else
                        Dim dr As DataRow = dtCsv.NewRow()
                        For k As Integer = 0 To rowValues.Count() - 1
                            dr(k) = rowValues(k).ToString()
                        Next
                        dtCsv.Rows.Add(dr)
                    End If
                End If
            Next
        End While
    End Using
    Return dtCsv
End Function

0

ผมเคยเขียนเมื่อเร็ว ๆ นี้parser CSVสำหรับ NET ที่ผมอ้างว่าขณะนี้เป็นวิธีที่เร็วใช้ได้เป็นแพคเกจ nuget A: Sylvan.Data.Csv

การใช้ไลบรารีนี้เพื่อโหลด a DataTableนั้นง่ายมาก

using var tr = File.OpenText("data.csv");
using var dr = CsvDataReader.Create(tr);
var dt = new DataTable();
dt.Load(dr);

สมมติว่าไฟล์ของคุณเป็นไฟล์ที่คั่นด้วยเครื่องหมายจุลภาคมาตรฐานพร้อมส่วนหัวนั่นคือทั้งหมดที่คุณต้องการ นอกจากนี้ยังมีตัวเลือกเพื่ออนุญาตให้อ่านไฟล์โดยไม่มีส่วนหัวและใช้ตัวคั่นสำรองเป็นต้น

นอกจากนี้ยังเป็นไปได้ที่จะให้สคีมาที่กำหนดเองสำหรับไฟล์ CSV เพื่อให้คอลัมน์สามารถถือว่าเป็นสิ่งอื่นที่ไม่ใช่stringค่า วิธีนี้จะทำให้DataTableคอลัมน์สามารถโหลดด้วยค่าที่สามารถใช้งานได้ง่ายขึ้นเนื่องจากคุณไม่ต้องบีบบังคับเมื่อคุณเข้าถึงคอลัมน์เหล่านั้น

var schema = new TypedCsvSchema();
schema.Add(0, typeof(int));
schema.Add(1, typeof(string));
schema.Add(2, typeof(double?));
schema.Add(3, typeof(DateTime));
schema.Add(4, typeof(DateTime?));

var options = new CsvDataReaderOptions { 
    Schema = schema 
};

using var tr = GetData();
using var dr = CsvDataReader.Create(tr, options);

TypedCsvSchemaเป็นการใช้งานICsvSchemaProviderซึ่งมีวิธีง่ายๆในการกำหนดประเภทของคอลัมน์ อย่างไรก็ตามยังเป็นไปได้ที่จะจัดทำแบบกำหนดเองICsvSchemaProviderเมื่อคุณต้องการให้ข้อมูลเมตาเพิ่มเติมเช่นความไม่ซ้ำกันหรือขนาดคอลัมน์ที่ถูก จำกัด เป็นต้น

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