Làm cách nào tôi có thể tải tệp CSV vào System.Data.DataTable
, tạo dữ liệu dựa trên tệp CSV?
Chức năng ADO.net thông thường có cho phép điều này không?
Làm cách nào tôi có thể tải tệp CSV vào System.Data.DataTable
, tạo dữ liệu dựa trên tệp CSV?
Chức năng ADO.net thông thường có cho phép điều này không?
Câu trả lời:
Đây là một lớp tuyệt vời sẽ sao chép dữ liệu CSV vào một cơ sở dữ liệu bằng cách sử dụng cấu trúc của dữ liệu để tạo DataTable:
Trình phân tích cú pháp chung di động và hiệu quả cho các tệp phẳng
Thật dễ dàng để cấu hình và dễ sử dụng. Tôi mong bạn hãy xem.
Tôi đã được sử dụng OleDb
nhà cung cấp. Tuy nhiên, nó có vấn đề nếu bạn đang đọc trong các hàng có giá trị số nhưng bạn muốn chúng được coi là văn bản. Tuy nhiên, bạn có thể khắc phục vấn đề đó bằng cách tạo một schema.ini
tệp. Đây là phương pháp của tôi, tôi đã sử dụng:
// 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;
}
}
Tôi đã quyết định sử dụng Csv Reader của Sebastien Lorion .
Đề xuất của Jay Riggs cũng là một giải pháp tuyệt vời, nhưng tôi không cần tất cả các tính năng mà Trình phân tích chung của Andrew Rissing cung cấp.
Sau khi sử dụng Csv Reader của Sebastien Lorion trong dự án của tôi trong gần một năm rưỡi, tôi đã thấy rằng nó đưa ra các ngoại lệ khi phân tích một số tệp csv mà tôi tin là được hình thành tốt.
Vì vậy, tôi đã chuyển sang Trình phân tích cú pháp chung của Andrew Rissing và nó dường như hoạt động tốt hơn nhiều.
Ngày nay, tôi chủ yếu sử dụng phương pháp mở rộng này để đọc văn bản được phân tách:
https://github.com/Core-Techs/Common/blob/master/CoreTechs.Common/Text/DelrictTextExtensions.cs#L22
https://www.nuget.org/packages/CoreTechs.Common/
Thí dụ:
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());
}
Này nó hoạt động 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;
}
Hình ảnh CSV
Bảng dữ liệu được nhập
Chúng tôi luôn sử dụng trình điều khiển Jet.OLEDB, cho đến khi chúng tôi bắt đầu chuyển sang các ứng dụng 64 bit. Microsoft đã không và sẽ không phát hành trình điều khiển Jet 64 bit. Đây là một giải pháp đơn giản mà chúng tôi đã đưa ra bằng cách sử dụng File.ReadAllLines và String.Split để đọc và phân tích tệp CSV và tải một DataTable theo cách thủ công. Như đã lưu ý ở trên, nó KHÔNG xử lý tình huống trong đó một trong các giá trị cột chứa dấu phẩy. Chúng tôi sử dụng phần lớn để đọc các tệp cấu hình tùy chỉnh - phần hay của việc sử dụng tệp CSV là chúng tôi có thể chỉnh sửa chúng trong 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);
}
đây là mã tôi sử dụng nhưng ứng dụng của bạn phải chạy với phiên bản thuần 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();
}
Bạn có thể đạt được nó bằng cách sử dụng Microsoft.VisualBasic.FileIO.TextFieldParser dll trong 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;
}
}
Tôi đã bắt gặp đoạn mã này sử dụng Linq và regex để phân tích tệp CSV. Bài viết giới thiệu hiện đã được hơn một năm rưỡi, nhưng chưa bắt gặp một cách gọn gàng hơn để phân tích CSV bằng Linq (và regex) hơn thế này. Thông báo trước là regex được áp dụng ở đây dành cho các tệp được phân tách bằng dấu phẩy (sẽ phát hiện dấu phẩy bên trong dấu ngoặc kép!) Và nó có thể không tốt cho các tiêu đề, nhưng có một cách để khắc phục những điều này). Lên đỉnh
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
Tùy chọn tốt nhất mà tôi đã tìm thấy và nó giải quyết các vấn đề trong đó bạn có thể cài đặt các phiên bản Office khác nhau và các vấn đề 32/64 bit như Chuck Bevitt đã đề cập , là FileHelpers .
Nó có thể được thêm vào tài liệu tham khảo dự án của bạn bằng NuGet và nó cung cấp giải pháp một lớp:
CommonEngine.CsvToDataTable(path, "ImportRecord", ',', true);
Đối với những người bạn muốn không sử dụng thư viện bên ngoài và không muốn sử dụng OleDB, hãy xem ví dụ bên dưới. Mọi thứ tôi tìm thấy là OleDB, thư viện bên ngoài hoặc đơn giản là phân tách dựa trên dấu phẩy! Đối với trường hợp của tôi, OleDB không hoạt động nên tôi muốn một cái gì đó khác biệt.
Tôi tìm thấy một bài viết của MarkJ đã tham chiếu phương thức Microsoft.VisualBasic.FileIO.TextFieldParser như được thấy ở đây . Bài viết được viết bằng VB và không trả về dữ liệu, vì vậy hãy xem ví dụ của tôi dưới đây.
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;
}
Câu trả lời rất cơ bản: nếu bạn không có một csv phức tạp có thể sử dụng hàm phân chia đơn giản thì điều này sẽ hoạt động tốt để nhập (lưu ý nhập này dưới dạng chuỗi, tôi sẽ chuyển đổi kiểu dữ liệu sau nếu tôi cần)
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;
}
Phương thức của tôi nếu tôi đang nhập một bảng có dấu phân tách chuỗi [] và xử lý vấn đề trong đó dòng hiện tại tôi đang đọc có thể đã đi đến dòng tiếp theo trong tệp csv hoặc tệp văn bản <- IN trong trường hợp tôi muốn lặp cho đến khi tôi nhận được đến tổng số dòng trong hàng đầu tiên (cột)
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;
}
Được sửa đổi từ Mr ChuckBevitt
Giải pháp làm việc:
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);
}
Đây là một giải pháp sử dụng trình điều khiển văn bản ODBC của 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
Sau khi điền, bạn có thể định giá các thuộc tính của dữ liệu, như Cột Tên, để sử dụng tất cả các quyền hạn của các đối tượng dữ liệu ADO.Net.
Trong VS2008 bạn có thể sử dụng Linq để đạt được hiệu quả tương tự.
LƯU Ý: Đây có thể là một bản sao của câu hỏi SO này .
Không thể cưỡng lại việc thêm spin của riêng tôi vào đây. Điều này tốt hơn và nhỏ gọn hơn nhiều so với những gì tôi đã sử dụng trong quá khứ.
Giải pháp này:
Đây là những gì tôi nghĩ ra:
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
Nó phụ thuộc vào một phương thức mở rộng ( Unique
) để xử lý các tên cột trùng lặp được tìm thấy như câu trả lời của tôi trong Cách nối các số duy nhất vào danh sách các chuỗi
Và đây là BlankToNothing
chức năng của trình trợ giúp:
Public Function BlankToNothing(ByVal Value As String) As Object
If String.IsNullOrEmpty(Value) Then Return Nothing
Return Value
End Function
Với Cinchoo ETL - một thư viện mã nguồn mở, bạn có thể dễ dàng chuyển đổi tệp CSV thành DataTable với một vài dòng mã.
using (var p = new ChoCSVReader(** YOUR CSV FILE **)
.WithFirstLineHeader()
)
{
var dt = p.AsDataTable();
}
Để biết thêm thông tin, vui lòng truy cập bài viết codeproject .
Hy vọng nó giúp.
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
đã được đổi tên CsvConfiguration
để tránh xung đột không gian tên. Bản demo của câu trả lời này hoạt động: dotnetfiddle.net/sdwc6i
Tôi sử dụng một thư viện có tên ExcelDataReader, bạn có thể tìm thấy nó trên NuGet. Hãy chắc chắn cài đặt cả phần mở rộng ExcelDataReader và ExcelDataReader.DataSet (phần sau cung cấp phương thức AsDataSet được yêu cầu được tham chiếu bên dưới).
Tôi gói gọn mọi thứ trong một hàm, bạn có thể sao chép nó trực tiếp vào mã của mình. Cung cấp cho nó đường dẫn đến tệp CSV, nó sẽ giúp bạn có được một tập dữ liệu với một bảng.
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
thay vì đường dẫn tệp. DataTable mà OP yêu cầu dễ dàng trích xuất từ Bộ dữ liệu như thế này:result.Tables[0]
Chỉ cần chia sẻ phương pháp mở rộng này, tôi hy vọng rằng nó có thể giúp được ai đó.
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;
}
Sử dụng chức năng này, một chức năng giải quyết tất cả các vấn đề về dấu phẩy và trích dẫn:
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
Gần đây tôi đã viết một trình phân tích cú pháp CSV cho .NET mà tôi cho rằng hiện tại là gói nhanh nhất có sẵn dưới dạng gói nuget : Sylvan.Data.Csv .
Sử dụng thư viện này để tải a DataTable
là cực kỳ dễ dàng.
using var tr = File.OpenText("data.csv");
using var dr = CsvDataReader.Create(tr);
var dt = new DataTable();
dt.Load(dr);
Giả sử tệp của bạn là một tệp được phân tách bằng dấu phẩy chuẩn với các tiêu đề, đó là tất cả những gì bạn cần. Ngoài ra còn có các tùy chọn để cho phép đọc tệp mà không cần tiêu đề và sử dụng các dấu phân cách thay thế, v.v.
Cũng có thể cung cấp một lược đồ tùy chỉnh cho tệp CSV để các cột có thể được coi là một cái gì đó không phải là string
giá trị. Điều này sẽ cho phép các DataTable
cột được tải với các giá trị có thể dễ dàng làm việc hơn, vì bạn sẽ không phải ép buộc chúng khi bạn truy cập chúng.
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
là một triển khai trong ICsvSchemaProvider
đó cung cấp một cách đơn giản để xác định các loại cột. Tuy nhiên, cũng có thể cung cấp một tùy chỉnh ICsvSchemaProvider
khi bạn muốn cung cấp nhiều siêu dữ liệu hơn, chẳng hạn như tính duy nhất hoặc kích thước cột bị ràng buộc, v.v.