CDataFile - 一个易于使用的类,用于读取 CSV 或文本分隔格式的数值数据。
此类读取数值数据并将其存储以方便访问。可以从您的任何数据归约例程中按(行,列)访问数据。
引言
本文介绍了 CDataFile
类。CDataFile
是一个类,旨在提供一个接口,用于使用和操作 CSV(逗号分隔值)或其他文本分隔的表格数据文件。CDataFile
最初于 2002 年 11 月发布,并经过了多次改进和增强,最终被完全重新设计。此实现已删除了对 MFC 的所有依赖,现在充分利用了标准模板库 (STL)。在整个类中,您会发现一些有趣的技巧以及对谓词、函数对象和 STL 算法的一些创造性使用。但最重要的是,您会发现一个强大的接口,该接口既方便新手也方便经验丰富的程序员。
为什么选择 CDataFile?
当我开始寻找一个类来读取 CSV 和其他文本数据时,我有以下标准:
- 该类在解析、查找和数据操作方面必须非常快速。
- 该类必须提供一个简单的(列,行)类型接口。
- 该类必须能够有效地处理超过 100MB 的数据文件。
出乎我的意料,我很难找到满足我标准的。我发现了一些使用 OBDC 甚至 DOM 的东西,但没有轻量级、易于使用或非常快速的东西。所以,我决定自己写一个,CDataFile
就是结果。
使用 CDataFile
要使用 CDataFile
,您需要将 DataFile.cpp 和 DataFile.h 添加到您的项目中,并包含以下头文件:
#include "DataFile.h"
我在下表中列出了 CDataFile
的成员函数和运算符。请注意,在此类中,variable(变量)一词与 field(字段)或 column(列)同义,而 sample(样本)一词与 record(记录)或 row(行)同义。
CDataFile 成员函数
函数 | 描述 |
AppendData |
将数据追加到指定的变量。 |
ClearData |
清除 CDataFile 中的所有数据。 |
CreateVariable |
在 CDataFile 中创建一个新变量。 |
DeleteVariable |
从 CDataFile 中删除一个变量。 |
FromVector |
一个静态函数,用于从 vector 创建 CDataFile 。 |
GetData |
从 CDataFile 获取数据。 |
GetLastError |
获取 CDataFile 遇到的最后一个错误。 |
GetNumberOfSamples |
获取变量中包含的样本数。 |
GetNumberOfVariables |
获取 CDataFile 中的变量数。 |
GetReadFlags |
获取当前的读取标志。 |
GetVariableIndex |
获取指定变量的查找索引。 |
GetVariableName |
获取指定位置变量的名称。 |
ReadFile |
读取文件内容并将其存储在 CDataFile 中。 |
SetData |
在 CDataFile 中设置数据。 |
SetDelimiter |
设置用于解析文件的分隔符。 |
SetReadFlags |
设置 CDataFile 的读取标志。 |
SetReserve |
设置 CDataFile 在需要重新分配内存之前的连续内存容量。 |
WriteFile |
将 CDataFile 的内容写入文件。 |
CDataFile 运算符
运算符 | 描述 |
operator() |
根据调用的重载不同,从 CDataFile 获取数据。 |
operator[][] |
获取指定位置数据的引用。(注意:仅对 DOUBLE 模式有效,对 STRING 无效) |
operator+ |
合并 CDataFile (s) 的内容。 |
operator+= |
将另一个 CDataFile 的内容追加到 CDataFile 。 |
operator= |
将 CDataFile 的内容设置为另一个 CDataFile 的内容。 |
operator<< |
将 CDataFile 的内容放入流。 |
operator>> |
从流中提取 CDataFile 的内容。 |
构造 CDataFile
CDataFile(void); CDataFile(const int& dwReadFlags); CDataFile(const char* szFilename, const int& dwReadFlags = DF::RF_READ_AS_DOUBLE); CDataFile(const DF::DF_SELECTION& df_selection); CDataFile(const CDataFile& df);
参数
dwReadFlags
决定数据如何被读取和存储在
CDataFile
中。可以是以下任意组合:RF_READ_AS_DOUBLE
(如果同时设置了RF_READ_AS_STRING
,则此标志优先)RF_READ_AS_STRING
RF_APPEND_DATA
(如果同时设置了RF_REPLACE_DATA
,则此标志优先)RF_REPLACE_DATA
szFilename
要读取的数据文件的完整路径。
df_selection
另一个
CDataFile
的子集,构造函数将在此基础上设置初始值。df
另一个
CDataFile
,构造函数将在此基础上设置初始值。
备注
CDataFile
的任何构造函数都不会抛出异常,而是会在内部捕获任何异常。用户可以选择调用 CDataFile::GetLastError()
来检索由于 CDataFile
构造函数异常而产生的任何错误信息。
示例
// Construct an empty CDataFile: CDataFile df; // Construct a CDataFile to read a CSV file containing data of type double: CDataFile df("C:\\MyData.csv"); // Construct a CDataFile to read a CSV file containing data of type string: CDataFile df("C:\\MyStringData.csv", DF::RF_READ_AS_STRING); // Construct an empty CDataFile that will read string data and append // data with each subsequent file read: CDataFile df(DF::RF_READ_AS_STRING | DF::RF_APPEND_DATA); // Construct a CDataFile from a subset of another CDataFile: CDataFile df1("C:\\MyData.csv"); CDataFile df2(df1(iLeftColumn, iTopRow, iRightColumn, iBottomRow));
CDataFile::AppendData()
AppendData()
成员函数将数据追加到指定变量的末尾。您可以逐个追加值,或追加整个 vector 的内容。AppendData()
如果成功将返回 true,如果遇到错误将返回 false。
bool AppendData(const int& iVariable, const char* szValue); bool AppendData(const int& iVariable, const double& value); bool AppendData(const int& iVariable, const std::vector<double>& vData); bool AppendData(const int& iVariable, const std::vector<std::string>& vszData);
参数
iVariable
要追加数据变量的索引。
szValue
要追加的值。(假定为
DF::RF_READ_AS_STRING
。)value
要追加的值。(假定为
DF::RF_READ_AS_DOUBLE
。)vData
一个常量引用,包含要追加的值的 vector。(假定为
DF::RF_READ_AS_DOUBLE
。)vszData
一个常量引用,包含要追加的值的 vector。(假定为
DF::RF_READ_AS_STRING
。)
返回值
如果函数成功,则返回true
,如果遇到错误,则返回false
。
备注
如果用户追加string
数据,则将其追加到CDataFile
的内部string
vector 中;如果用户追加double
类型的值,则数据将被分配到类内部的double
vector 中。
示例
// To append 0.2123 to the variable at index 0: if(!df.AppendData(0, 0.2123)) cout << df.GetLastError(); // To append the contents of a std::vector<double>, v, to the variable at index 4: if(!df.AppendData(4,v); cout << df.GetLastError();
CDataFile::ClearData()
ClearData()
成员函数负责清除 CDataFile
中的所有数据,回收任何已分配的内存,并将类的内部缓冲区大小重置为零。
void ClearData(void);
备注
当读取新数据文件时,该类将回收等于CDataFile::SetReserve()
指定大小的连续内存块,否则内部缓冲区容量将为零。该类会在需要时分配内部缓冲区存储,但如果类需要读取过大的文件,可能会出现本文 [^] 中研究的行为。在调用CDataFile::ReadFile()
之前,使用具有足够容量的CDataFile::SetReserve()
将可以消除此行为。
CDataFile::ClearData()
在CDataFile
被销毁时自动调用。
示例
// Flush the contents of a CDataFile, df.
df.ClearData();
CDataFile::CreateVariable()
CreateVariable()
成员函数提供了一种向 CDataFile
末尾追加新变量的方法。可以将其视为在 Excel 中向数据添加新列。您可以创建具有预定大小和初始值的变量,也可以从现有 vector 创建变量。
bool CreateVariable(const char* szName, const double& initial_value, const int& iSize = 0); bool CreateVariable(const char* szName, const std::string& initial_value, const int& iSize = 0); bool CreateVariable(const char* szName, const std::vector<double>& vData); bool CreateVariable(const char* szName, const std::vector<std::string>& vszData);
参数
szName
您想为变量指定的名称。可以将其视为 Excel 中的列标签或数据库中的字段名。
initial_value
您想让所有新数据包含的值。可以将其视为将一列中的所有行都设置为此初始值。
iSize
您希望变量包含的样本(行或记录)数量。
vData
一个包含要分配给新变量数据的
double
类型 vector。vszData
一个包含要分配给新变量数据的
string
类型 vector。
返回值
如果函数成功,则返回true
,如果遇到错误,则返回false
。
备注
新变量的默认大小为 0。用户必须随后调用CDataFile::AppendData()
来向变量添加值。如果用户尝试在大小为 0 的变量上调用CDataFile::SetData()
,则会发生错误。
示例
// Create a variable in df named "MyVar" // with 27 samples initialized to 0.0. if(!df.CreateVariable("MyVar", 0.0, 27)) cout << df.GetLastError(); // Create a variable in df named "My Vector Var" // containing the data in vector, v. if(!df.CreateVariable("My Vector Var",v); cout << df.GetLastError();
CDataFile::DeleteVariable()
DeleteVariable()
成员函数用于从 CDataFile
中删除变量。可以将其视为在 Excel 中删除列,或在数据库中删除整个字段。
bool DeleteVariable(const int& iVariable);
参数
iVariable
要从
CDataFile
中删除的变量的索引。
返回值
如果函数成功,则返回true
,如果遇到错误,则返回false
。
备注
使用 CDataFile::GetLastError()
来检索任何错误信息。
示例
// Delete the variable at index 3 from CDataFile, df. if(!df.DeleteVariable(3)) cout << df.GetLastError();
CDataFile::FromVector()
static
成员函数 FromVector()
提供了一种从现有数据 vector 创建 CDataFile
对象的方法。当使用 CDataFile::operator+
和 CDataFile::operator+=
时,此函数特别有用。
static CDataFile FromVector(const char* szVariableName, const std::vector<double>& vData); static CDataFile FromVector(const char* szVariableName, const std::vector<std::string>& vData);
参数
szVariableName
您想为变量指定的名称。可以将其视为 Excel 中的列标签或数据库中的字段名。
vData
一个包含要分配给新变量数据的
double
或string
类型 vector。
返回值
返回一个包含结果变量的 CDataFile
。
备注
当需要将 vector 数据转换为 CDataFile
时,请使用此静态函数。
示例
// Add the contents of vector, v, to CDataFile, df, using operator += df += CDataFile::FromVector("MyVectorData", v); // Combine the contents of vector, v1, // and vector, v2, and set the CDataFile, df, // equal to the result. df = CDataFile::FromVector("My V1 Data", v1) + CDataFile::FromVector("My V2 Data", v2);
CDataFile::GetData()
为 GetData()
成员函数提供了几个重载。这是为了允许用户以极大的灵活性检索数据。您可以按变量索引或变量名获取数据,还可以获取单个样本或所有样本。
double GetData(const int& iVariable, const int& iSample); double GetData(const char* szVariableName, const int& iSample); int GetData(const int& iVariable, std::vector<double>& rVector); int GetData(const char* szVariableName, std::vector<double>& rVector); int GetData(const int& iVariable, const int& iSample, char* lpStr); int GetData(const char* szVariableName, const int& iSample, char* lpStr); int GetData(const int& iVariable, const int& iSample, std::string& rStr); int GetData(const char* szVariableName, const int& iSample, std::string& rStr); int GetData(const int& iVariable, std::vector<std::string>& rVector); int GetData(const char* szVariableName, std::vector<std::string>& rVector);
参数
iVariable
要从中检索数据的变量的索引。
iSample
要检索的样本编号(记录或行,从 0 开始索引)。
rVector
一个 vector 的引用,包含接收数据的正确数据类型。
lpStr
一个指向字符串缓冲区的指针,用于接收数据。
rStr
一个
std::string
的引用,用于接收数据。
返回值
double GetData(const int& iVariable, const int& iSample); double GetData(const char* szVariableName, const int& iSample);如果成功,则返回等于指定位置数据的
double
类型值;如果遇到错误,则返回DF::ERRORVALUE
。int GetData(const int& iVariable, std::vector<double>& rVector); int GetData(const char* szVariableName, std::vector<double>& rVector); int GetData(const int& iVariable, std::vector<std::string>& rVector); int GetData(const char* szVariableName, std::vector<std::string>& rVector);如果成功,则返回
rVector
的新大小;如果遇到错误,则返回 -1。int GetData(const int& iVariable, const int& iSample, char* lpStr); int GetData(const char* szVariableName, const int& iSample, char* lpStr); int GetData(const int& iVariable, const int& iSample, std::string& rStr); int GetData(const char* szVariableName, const int& iSample, std::string& rStr);如果成功,则返回
lpStr
或rStr
的新长度;如果遇到错误,则返回 -1。
备注
如果使用double
类型参数调用GetData()
,则假定为DF::RF_READ_AS_DOUBLE
。当使用char*
或std::string
类型参数调用时,假定为DF::RF_READ_AS_STRING
。
示例
// Initialize a variable, d, to sample 7 of "MyVar" from CDataFile, df. double d = df.GetData("MyVar", 7); if(d == DF::ERRORVALUE) cout << df.GetLastError(); // Get string data from sample 3 of variable 9 from CDataFile, df. CString szValue = ""; int iLength = 0; iLength = df.GetData(9, 3, szValue.GetBuffer(0)); szValue.ReleaseBuffer(); if(iLength == -1) cout << df.GetLastError(); else { //... do something } // Get all the data from "My Variable 2" out of CDataFile, // df, and put it in vector, vData. std::vector<double> vData; int iSize = df.GetData("My Variable 2", vData); if(iSize == -1) cout << df.GetLastError(); else { //... do something }
CDataFile::GetLastError()
GetLastError()
成员函数提供了一种从 CDataFile
中提取有关类遇到的最后一个错误的信息的方法。
const char* GetLastError(void) const;
返回值
返回一个 const char*
,其中包含有关类遇到的最后一个错误的信息。
示例
// Display the last error encountered by CDataFile, df.
cout << df.GetLastError();
CDataFile::GetNumberOfSamples()
GetNumberOfSamples()
成员函数提供了一种确定任何给定变量中包含的样本数量的方法。
int GetNumberOfSamples(const int& iVariable) const;
参数
iVariable
要获取样本数量的变量的索引。
返回值
如果函数成功,则返回样本数;如果遇到错误,则返回 -1。
备注
使用 CDataFile::GetLastError()
来检索任何错误信息。
示例
// Get the number of samples contained // in the variable at index 3 from CDataFile, df. int nSamps = df.GetNumberOfSamples(3); if(nSamps==-1) cout << df.GetLastError();
CDataFile::GetNumberOfVariables()
提供 GetNumberOfVariables()
成员函数,以便用户可以获取 CDataFile
中当前包含的变量数量。
int GetNumberOfVariables(void) const;
返回值
返回 CDataFile
中包含的变量数量。
示例
// Get the number of variables contained in CDataFile, df. int nVars = df.GetNumberOfVariables();
CDataFile::GetReadFlags()
GetReadFlags()
成员函数提供了一种获取当前已设置或清除的读取标志的方法。
int GetReadFlags(void) const;
返回值
返回当前的读取标志。
备注
该函数返回一个int
,其中包含编码的标志。使用按位&
运算符来确定实际设置了哪些标志。
示例
// Check to see if the user has set DF::RF_APPEND_DATA cout << "Append mode is " << (df.GetReadFlags() & DF::RF_APPEND_DATA ? "" : "NOT " ) << "set!";
CDataFile::GetVariableIndex()
GetVariableIndex()
成员函数提供了一种根据变量名和/或其他信息查找变量索引的方法。
int GetVariableIndex(const char* szVariableName, const int& iStartingIndex = 0); int GetVariableIndex(const char* szVariableName, const char* szSourceFilename, const int& iStartingIndex = 0);
参数
szVariableName
要查找索引的变量的名称。
iStartingIndex
开始搜索的索引。
szSourceFilename
变量源自的文件的名称。
返回值
如果函数成功,则返回变量的索引(从 0 开始);如果遇到错误,则返回 -1。
备注
GetVariableIndex()
将返回变量名的第一个实例。如果您的数据中有同名变量,而您需要第一个实例以外的变量名实例,您应该使用iStartingIndex
来偏移搜索。您可能在
CDataFile
中拥有来自不同源文件的数据(例如,使用DF::RF_APPEND_DATA
)。在这些情况下,您可能需要某个特定源文件的变量实例。在这种情况下,您将使用szSourceFilename
提供的重载。
示例
// Get the index of "MyVar" from CDataFile, df. int iVar = df.GetVariableIndex("MyVar"); if(iVar == -1) cout << df.GetLastError(); // Get the index of "My Var 2" occurring after // variable 3 whose source file is "C:\data.csv". int iVar = df.GetVariableIndex("My Var 2", "C:\\data.csv", 3); if(iVar == -1) cout << df.GetLastError();
CDataFile::GetVariableName()
GetVariableName()
成员函数提供了一种根据变量(或字段)的索引(或从 0 开始的列号)查找其名称的方法。
int GetVariableName(const int& iVariable, char* lpStr); int GetVariableName(const int& iVariable, std::string& rStr);
参数
iVariable
要获取名称的变量的索引。
lpStr
一个指向字符串缓冲区的指针,用于接收名称。
rStr
一个用于接收名称的字符串。
返回值
如果函数成功,则返回变量名的长度;如果遇到错误,则返回 -1。
备注
使用 CDataFile::GetLastError()
来检索任何错误信息。
示例
// Get the variable name at index 3. CString szVarName = ""; int iLength = df.GetVariableName(3, szVarName.GetBuffer(0)); szVarName.ReleaseBuffer(); // or... std::string szVar = ""; iLength = df.GetVariableName(3, szVar); if(iLength==-1) cout << df.GetLastError(); else cout << szVar.c_str();
CDataFile::ReadFile()
ReadFile()
成员函数提供了一种通过封装所有文件 IO 操作来轻松读取数据文件的方法。
bool ReadFile(const char* szFilename); bool ReadFile(const char* szFilename, const unsigned& dwReadFlags);
参数
szFileName
文件的完整路径。
dwReadFlags
用于指定如何读取文件的标志。(有关更多信息,请参阅 构造 CDataFile。)
返回值
如果函数成功,则返回true
,如果遇到错误,则返回false
。
备注
使用 CDataFile::GetLastError()
来检索任何错误信息。
示例
// Read the contents of "C:\test.csv" into CDataFile, df. if(!df.ReadFile("C:\\test.csv")) cout << df.GetLastError(); // Append the data from "C:\test2.csv" to CDadaFile, df. if(!df.ReadFile("C:\\test2.csv", DF::RF_APPEND_DATA)) cout << df.GetLastError();
CDataFile::SetData()
SetData()
成员函数提供了一种设置 CDataFile
中存储的数据值的方法。
bool SetData(const int& iVariable, const int& iSample, const double& value); bool SetData(const int& iVariable, const int& iSample, const char* szValue);
参数
iVariable
要设置数据的变量。
iSample
要设置数据的样本。
value
将数据设置为此值。(假定为
DF::RF_READ_AS_DOUBLE
)szValue
将数据设置为此值。(假定为
DF::RF_READ_AS_STRING
)
返回值
如果函数成功,则返回true
,如果遇到错误,则返回false
。
备注
使用 CDataFile::GetLastError()
来检索任何错误信息。
示例
// Set variable 3, sample 0 to 2.121 in CDataFile, df. if(!df.SetData(3,0,2.121)) cout << df.GetLastError();
CDataFile::SetDelimiter()
SetDelimiter()
成员函数提供了一种设置 CDataFile
中值分隔符的方法。
void SetDelimiter(const char* delim);
参数
delim
用于解析数据的字符。
示例
// Set the delimiter in CDataFile, df, to 'tab'. df.SetDelimiter("\t");
CDataFile::SetReserve()
SetReserve()
成员函数提供了一种设置 CDataFile
容量的方法。
void SetReserve(const int& nReserve);
参数
nReserve
要为其预留空间的元素数量。
示例
// Set the reserve of CDataFile, df, to 1000000. df.SetReserve(1000000);
CDataFile::WriteFile()
WriteFile()
成员函数提供了一种将 CDataFile
写入磁盘的简化方法。
bool WriteFile(const char* szFilename, const char* szDelim = ",");
参数
szFilename
目标文件的完整路径。
szDelim
用于分隔数据值的分隔符。
返回值
如果函数成功,则返回true
,如果遇到错误,则返回false
。
备注
使用
CDataFile::GetLastError()
来检索任何错误信息。
示例
// Write the contents of CDataFile, df, to "C:\test.csv". if(!df.WriteFile("C:\\test.csv")) cout << df.GetLastError();
CDataFile::operator()
()
运算符提供了一种从 CDataFile
中轻松提取数据的方法。
double operator()(const int& iVariable, const int& iSample); int operator()(const int& iVariable, const int& iSample, char* lpStr); DF::DF_SELECTION operator()(const int& left, const int& top, const int& right, const int& bottom);
参数
iVariable
要获取数据的变量。
iSample
要获取数据的样本。
lpStr
一个用于接收数据的字符串缓冲区。
left
,top
,right
,bottom
要从中获取结果选择的坐标。可以将其视为在 Excel 中突出显示一个单元格范围。
返回值
double operator()(const int& iVariable, const int& iSample);
如果成功,则返回指定位置的值;如果遇到错误,则返回
DF::ERRORVALUE
。
int operator()(const int& iVariable, const int& iSample, char* lpStr);
如果成功,则返回
lpStr
的新长度;如果遇到错误,则返回 -1。
DF::DF_SELECTION operator()(...);
返回一个包含指定范围内值的选择(
DF_SELECTION
)。
备注
使用 CDataFile::GetLastError()
来检索任何错误信息。
示例
// Set d equal to the data at (2,4) from CDataFile, df. d = df(2,4); // Get the string data contained at (1,7). CString szData = ""; int iLength = df(1,7,szData.GetBuffer(0)); szData.ReleaseBuffer(); // Create a new CDataFile from the range (0,0,9,120). dfNew(df(0,0,9,120));
CDataFile::operator[][]
[][]
运算符提供了一种从 CDataFile
中轻松提取 double
类型数据的方法。
返回值
返回指定位置数据的引用。
备注
此运算符仅对 DF::RF_READ_AS_DOUBLE
有效。由于返回的是实际引用,因此您可以赋值和读取值。
示例
// Set d equal to the data at variable 2, sample 4 from CDataFile, df. double d = df[2][4]; // Set the data at variable 9, sample 0, from CDataFile, df, equal to d. df[9][0] = d;
CDataFile::operator+
+
运算符提供了一种合并多个 CDataFile
对象内容的方法。
CDataFile operator+ (const CDataFile&) const;
示例
// Set CDataFile, df, to the combined data of df2, df3, and df4.
df = df2 + df3 + df4;
CDataFile::operator+=
+=
运算符提供了一种将一个 CDataFile
追加到另一个 CDataFile
的方法。
CDataFile& operator+=(const CDataFile&);
示例
// Append the contents of CDataFile, df2, to CDataFile, df.
df += df2;
CDataFile::operator=
=
运算符将一个 CDataFile
的内部状态设置为等于另一个 CDataFile
。
CDataFile& operator =(const CDataFile&); CDataFile& operator =(const DF::DF_SELECTION&);
示例
// Set CDataFile, df2, equal to CDataFile, df. df2 = df1; // Set df3 equal to the selection df(0,0,4,120). df3 = df(0,0,4,120);
CDataFile::operator<<
<<
运算符将 CDataFile
的内容放入流。
std::ostream& operator << (std::ostream&, const CDataFile&); std::ostream& operator << (std::ostream&, const DF::DF_SELECTION&);
示例
// Put CDataFile, df, to outStream. outStream << df; // Put the selection (0,0,5,15) to outStream. outStream << df(0,0,5,15);
CDataFile::operator>>
>>
运算符从流中获取 CDataFile
的内容。
std::istream& operator >> (std::istream&, CDataFile&);
示例
// Get CDataFile, df, from inStream.
inStream >> df;