ฉันจะโหลดไฟล์ CSV ลงใน a System.Data.DataTable
สร้างข้อมูลได้อย่างไรโดยอ้างอิงจากไฟล์ CSV
ฟังก์ชั่นปกติของ ADO.net อนุญาตหรือไม่
ฉันจะโหลดไฟล์ CSV ลงใน a System.Data.DataTable
สร้างข้อมูลได้อย่างไรโดยอ้างอิงจากไฟล์ CSV
ฟังก์ชั่นปกติของ ADO.net อนุญาตหรือไม่
คำตอบ:
นี่คือคลาสที่ยอดเยี่ยมที่จะคัดลอกข้อมูล CSV ลงใน DataTable โดยใช้โครงสร้างของข้อมูลเพื่อสร้าง DataTable:
ตัวแยกวิเคราะห์ทั่วไปแบบพกพาและมีประสิทธิภาพสำหรับไฟล์แบน
ง่ายต่อการกำหนดค่าและใช้งานง่าย ฉันอยากให้คุณดู
ฉันใช้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;
}
}
ฉันได้ตัดสินใจที่จะใช้เซบาสเตียน Lorion ของ Csv อ่าน
ข้อเสนอแนะของ Jay Riggs ก็เป็นทางเลือกที่ยอดเยี่ยมเช่นกัน แต่ฉันไม่ต้องการคุณสมบัติทั้งหมดที่Generic Parser ของ Andrew Rissingมอบให้
หลังจากใช้Csv ReaderของSebastien Lorionในโครงการของฉันเกือบหนึ่งปีครึ่งฉันพบว่ามันมีข้อยกเว้นเมื่อทำการวิเคราะห์ไฟล์ csv บางไฟล์ที่ฉันเชื่อว่ามีรูปแบบที่ดี
ดังนั้นฉันเปลี่ยนไปใช้Generic Parser ของ Andrew Rissingและดูเหมือนว่าจะทำได้ดีขึ้นมาก
วันนี้ฉันส่วนใหญ่ใช้วิธีการขยายนี้เพื่ออ่านข้อความที่คั่นด้วย:
https://www.nuget.org/packages/CoreTechs.Common/
ตัวอย่าง:
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());
}
เฮ้มันใช้งานได้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
นำเข้าตารางข้อมูล
เรามักจะใช้ไดรเวอร์ 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);
}
นี่คือรหัสที่ฉันใช้ แต่แอปของคุณต้องทำงานด้วย 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();
}
คุณสามารถทำได้โดยใช้ 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;
}
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;
}
}
ฉันเจอโค้ดนี้ที่ใช้ 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
ตัวเลือกที่ดีที่สุดที่ฉันได้พบและช่วยแก้ปัญหาที่คุณอาจจะมีรุ่นที่แตกต่างของการติดตั้ง Office และ 32 ประเด็น / 64-bit เช่นโยน Bevitt กล่าวถึงเป็นFileHelpers
สามารถเพิ่มลงในการอ้างอิงโครงการของคุณโดยใช้ NuGet และให้บริการโซลูชั่นแบบหนึ่งซับ:
CommonEngine.CsvToDataTable(path, "ImportRecord", ',', true);
สำหรับผู้ที่คุณไม่ต้องการใช้ห้องสมุดภายนอกและไม่ต้องการใช้ 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;
}
คำตอบขั้นพื้นฐานมาก: หากคุณไม่มี 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;
}
ดัดแปลงจาก 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);
}
นี่คือโซลูชันที่ใช้โปรแกรมควบคุมข้อความ 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
ไม่สามารถต้านทานการเพิ่มสปินของฉันเองในสิ่งนี้ มันดีกว่าและกะทัดรัดกว่าที่ฉันเคยใช้ในอดีตมาก
วิธีนี้:
นี่คือสิ่งที่ฉันมาด้วย:
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
ด้วยCinchoo ETL - ไลบรารีโอเพ่นซอร์สคุณสามารถแปลงไฟล์ CSV เป็น DataTable ด้วยโค้ดไม่กี่บรรทัด
using (var p = new ChoCSVReader(** YOUR CSV FILE **)
.WithFirstLineHeader()
)
{
var dt = p.AsDataTable();
}
สำหรับข้อมูลเพิ่มเติมกรุณาเยี่ยมชมบทความcodeproject
หวังว่ามันจะช่วย
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;
}
Configuration
ถูกเปลี่ยนชื่อเป็นCsvConfiguration
เพื่อหลีกเลี่ยงความขัดแย้งของเนมสเปซ การสาธิตการทำงานของคำตอบนี้: dotnetfiddle.net/sdwc6i
ฉันใช้ไลบรารีชื่อ 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();
}
}
MemoryStream
แทนเส้นทางไฟล์ DataTable ที่ OP ขอแยกออกได้ง่ายจาก DataSet ดังนี้:result.Tables[0]
เพียงแชร์วิธีการส่วนขยายนี้ฉันหวังว่าจะสามารถช่วยคนอื่นได้
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;
}
ใช้สิ่งนี้หนึ่งฟังก์ชั่นแก้ปัญหาทั้งหมดของเครื่องหมายจุลภาคและพูด:
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;
}
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
ผมเคยเขียนเมื่อเร็ว ๆ นี้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
เมื่อคุณต้องการให้ข้อมูลเมตาเพิ่มเติมเช่นความไม่ซ้ำกันหรือขนาดคอลัมน์ที่ถูก จำกัด เป็นต้น