在处理这类问题时,我喜欢将它们分解为与解析相关的部分。我正在使用一些标准库来为我完成一些工作。我还创建了一些结构来帮助组织数据信息。至于你的约会,我可以把它作为一个单一的std::string
但我选择打破date
分解为三种单独的类型并将它们存储到数据结构中,只是为了显示与解析相关的函数之一的功能。
我更喜欢做的是从文件中获取单行数据并将其保存到字符串中,或者获取文件的全部内容并将其保存到大缓冲区或字符串向量中,除非我处理不适用的特定类型的代码,例如解析wav
文件。然后在我完成读取后关闭文件句柄!然后,在获得所需的所有信息后,我宁愿解析字符串,因为它更容易解析,而不是在打开文件时尝试直接解析该文件。然后在解析字符串后,我们可以填充我们需要的数据类型。
我必须稍微修改您的数据文件以适应额外的空格,因此我将您的文件保存为文本文件,在单行文本中的每种数据类型之间只有一个空格。我也没有包含第一行(标题)信息,因为我完全省略了它。然而,这仍然应该作为如何为具有良好可读性、可重用性的应用程序设计良好的工作流程的指南,并尝试保持其可移植性和尽可能通用。现在,您一直在等待的事情;我的代码版本的演示:
#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <exception>
struct Date {
int month;
int day;
int year;
Date() = default;
Date( int monthIn, int dayIn, int yearIn ) :
month( monthIn ),
day( dayIn ),
year( yearIn )
{}
};
struct DataSheetItem {
int itemNumber;
Date date;
int quantity;
double costPerEach;
DataSheetItem() = default;
DataSheetItem( int itemNumberIn, Date& dateIn, int quantityIn, double costPerEachIn ) :
itemNumber( itemNumberIn ),
date( dateIn ),
quantity( quantityIn ),
costPerEach( costPerEachIn )
{}
};
std::vector<std::string> splitString( const std::string& s, char delimiter ) {
std::vector<std::string> tokens;
std::string token;
std::istringstream tokenStream( s );
while( std::getline( tokenStream, token, delimiter ) ) {
tokens.push_back( token );
}
return tokens;
}
void getDataFromFile( const char* filename, std::vector<std::string>& output ) {
std::ifstream file( filename );
if( !file ) {
std::stringstream stream;
stream << "failed to open file " << filename << '\n';
throw std::runtime_error( stream.str() );
}
std::string line;
while( std::getline( file, line ) ) {
if ( line.size() > 0 )
output.push_back( line );
}
file.close();
}
DataSheetItem parseDataSheet( std::string& line ) {
std::vector<std::string> tokens = splitString( line, ' ' ); // First parse with delimeter of a " "
int itemNumber = std::stoi( tokens[0] );
std::vector<std::string> dateInfo = splitString( tokens[1], '/' );
int month = std::stoi( dateInfo[0] );
int day = std::stoi( dateInfo[1] );
int year = std::stoi( dateInfo[2] );
Date date( month, day, year );
int quantity = std::stoi( tokens[2] );
double cost = std::stod( tokens[3] );
return DataSheetItem( itemNumber, date, quantity, cost );
}
void generateDataSheets( std::vector<std::string>& lines, std::vector<DataSheetItem>& dataSheets ) {
for( auto& l : lines ) {
dataSheets.push_back( parseDataSheet( l ) );
}
}
int main() {
try {
std::vector<std::string> fileConents;
getDataSheetItemsFromFile( "test.txt", fileContents );
std::vector<DataSheetItem> data;
generateDataSheets( fileConents, data );
// test to see if info is correct
for( auto& d : data ) {
std::cout << "Item #: " << d.itemNumber << " Date: "
<< d.date.month << "/" << d.date.day << "/" << d.date.year
<< " Quantity: " << d.quantity << " Cost: " << d.costPerEach << '\n';
}
} catch( const std::runtime_error& e ) {
std::cerr << e.what() << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
NOTE这不适用于您当前的文件;这不考虑文本的第一行(标题信息),也不考虑数据字段之间的任何额外空白。如果在打开文件时添加一行文本并读取一行并忽略它,则执行循环以获取所有要添加到向量的字符串以返回;您的向量将包含信息,但由于所有额外的空格,它们不会位于向量的正确索引位置。这是你需要注意的事情!除此之外;这就是我基本上设计一个程序或应用程序来解析数据的方式。这无论如何都不是 100% 完整的证明,甚至可能不是 100% 无错误,但从快速浏览并通过我的调试器运行几次来看,它似乎没有任何明显的错误。运行时效率等方面也可能有一些改进的空间,但这只是基本解析的概括。