65.9K
CodeProject 正在变化。 阅读更多。
Home

读取文本文件(txt、csv、log、制表符、定长)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.39/5 (21投票s)

2008年6月9日

CPOL

5分钟阅读

viewsIcon

375134

downloadIcon

11469

本文主要关注如何高效地读取文本文件。它包括日志、csv、制表符分隔、定长文件等。我们不使用 StreamReader(.NET)/FileSystemObject(VB 6.0),而是将文件视为数据库表,通过查询来读取数据。

引言

在许多情况下,我们需要从文本文件(*.txt、*.CSV、*.tab)中读取和处理数据。最常用的方法是使用 StreamReader(.NET)/FileSystemObject(VB 6.0)逐行读取文件。

假设我们能够将文件视为数据库表并处理数据,那么上述方法存在许多缺点。

一些缺点包括:

  1. 连接环境。在进程完成之前锁定文件。
  2. 我们必须单独分割每一行才能获取单个数据列。处理包含逗号作为其数据一部分的行很困难。
    示例:“是,逗号在这里”,2,“你好,另一个逗号”,4,5,6
  3. 在读取该行并开始处理之前,我们没有选项来过滤数据。
  4. 计算文件中的记录数或特定类型的记录数需要读取整个文件。

我们可以列出许多此类缺点。使用数据库表查询的所有优点都可以被视为逐行读取的缺点。

现在,我们能否将文本文件视为数据库表来读取?
如果答案是否定的,我们就不会讨论这个话题。是的,我们可以将文件视为数据库表来读取,并且可以轻松克服上述所有缺点。

读取数据

这就像连接到数据库并从数据库表中查询数据一样简单。在这种情况下,我们将文本文件视为表,将包含的文件夹视为数据库。
读取数据的步骤如下:

  1. 打开数据库连接
    注意:连接字符串非常重要。它取决于我们尝试读取的文件类型。我们将在本文后面讨论这一点。
  2. 使用基本查询语言获取结果集。
  3. 循环遍历记录并读取字段。

C# 代码

DataSet myData = new DataSet();
string myXML;
string strFilePath = "C:\\";
string mySelectQuery = "SELECT * FROM SampleFile.CSV";
OleDbConnection myConnection = new OleDbConnection
		("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\\;" + 
                   "Extended Properties=\"text;HDR=YES;FMT=Delimited\"");
OleDbDataAdapter dsCmd = new OleDbDataAdapter(mySelectQuery, myConnection);
//Fill the DataSet object
dsCmd.Fill(myData, "CustomerOwners");
//Create a XML document with the table data
myData.WriteXml("D:\\TestXML.xml");
myConnection.Close();

VB 6.0 代码

'Set the database connection
objConnection.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
                   "Data Source=" & strFilePath & ";" & _
                   "Extended Properties=""text;HDR=YES;FMT=Delimited"""

'Query from the file assuming it as a database table
objRecordset.Open "SELECT * FROM " & fileName, _
          objConnection, adOpenStatic, adLockOptimistic, adCmdText

'Loop through the records and insert in to the table
Do Until objRecordset.EOF
    Debug.Print objRecordset.Fields.Item("Number")
    Debug.Print objRecordset.Fields.Item("Name")
objRecordset.MoveNext
Loop

我们将文本文件视为数据库表并将其上传到数据库。让我们看看这种方法的优点:

  1. 此过程速度很快。
  2. 我们需要注意的第一点是它处理数据行和分割数据的方式。假设文本列中包含逗号(上面列出的缺点的第 2 点),此过程会自动处理这种情况。我们无需单独处理。
  3. 如果我们给出选择条件,我们可以从文件中选择特定行集。
    示例:“SELECT * FROM SampleFile1.CSV WHERE Number >= 3
  4. 此时,可以清楚地理解,我们可以在查询中使用 WHEREHAVINGGROUP BY 子句,这有时非常有帮助。
    例如:“SELECT Number, Count(Name) FROM SampleFile1.CSV GROUP BY Number HAVING Count(Name) >= 1
  5. 在读取文件中的所有数据后,我们可以根据需要过滤数据。这可以使用 RowFilter/Filter 属性或 DataView/RecordSet 来完成。正如我所说,一旦我们将数据读入 Dataset/RecordSet,我们就可以对其执行所有可以执行的操作,包括过滤、排序等。

其他文件格式

我们讨论了如何读取逗号分隔值(CSV)文件。这种方法如何处理制表符分隔文件和定长文件?
一切取决于我们提供的连接字符串。让我们在此处查看连接字符串的详细信息。

Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\FolderName;
Extended Properties="text;HDR=YES;FMT=Delimited

提供程序 – 这代表数据库的类型。我们使用 OLEDB 连接类型。
数据源 – 文件夹被视为数据库。
扩展属性 – 这些属性将定义我们希望如何读取文件。

  • 其中第一部分定义了文件类型。在我们的例子中,尽管文件格式可以归类为逗号分隔、制表符分隔或定长数据,但它们都是简单的文本。如果我们要读取 Excel 文件,我们会使用 Excel x.x,其中 x.x 是版本号。
  • HDR(Header)– 用于指定是否存在标题。YES – 输入文件的第一行被视为标题,其余行被视为数据。NO – 从第一行开始视为数据。
  • FMT(Format)– 指定格式类型。它可以具有以下值:
    分隔 文件被视为逗号分隔文件。逗号是默认分隔符。
    Delimited(x) 文件被视为以分隔符“x”分隔的文件。
    TabDelimited 文件被视为制表符分隔的文件。
    FixedLength 读取具有指定字段固定长度的数据。您必须使用 Col1Col2 等属性指定列的宽度和类型。有关更多信息,请参阅 MSDN

如果指定的格式是“Delimited”,则默认字符是逗号(,)。它存储在注册表中。您可以在注册表 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Jet\4.0\Engines\Text\Format 下进行更改。
正如大家所说,不要随意修改注册表。Microsoft 提供了一个替代方法,即提供 Schema.ini 文件。如果我们读取 FixedLength 文件,我们必须使用 Schema.ini 文件。我们在 schema 文件中指定字段的长度。

从多个文件读取

如果您有多个文件,并且需要根据公共列合并或过滤数据,我们可以像在 SQL 中一样进行操作。我们可以连接表并获取合并后的数据。请记住,输出将是两个文件中行的 CROSS JOIN。确保根据公共列过滤数据。

示例

SampleFile1.CSV –(EmpIDNameAddress
SampleFile2.CSV –(EmpIDSalaryMonth

//Where clause is used to get ‘Natural Join’
string mySelectQuery = "SELECT * FROM SampleFile.CSV As Sample1, _
			SampleFile2.CSV As Sample2 " + _
                       	"Where Sample1.Number=Sample2.Number";

'Where clause is used to get ‘Natural Join’
objRecordset.Open "SELECT * FROM SampleFile.CSV As Sample1, _
		SampleFile2.CSV As Sample2 " & _ 
                  "Where Sample1.Number=Sample2.Number", _
                  objConnection, adOpenStatic, adLockOptimistic, adCmdText

请注意,在 join 查询中,尤其是在表具有相同名称的列时,需要使用别名(SampleFile.CSV AS Sample1)。在我们的例子中,表名(文件名)之间有点(.)。因此,查询解析器可能会被表中的点误导,并将“CSV.Number”视为列名。

历史

  • 2008年6月9日:初始帖子
© . All rights reserved.