Обычно, когда я читаю и записываю данные из файлов, особенно при их чтении;Мне нравится создавать структуру данных, которая будет представлять информацию, которую я извлекаю из файла.Вы должны будете знать, как файл структурирован, чтобы прочитать содержимое из него.Как правило, есть 3 разных способа;вы можете читать по одной строке за раз, вы можете читать построчно, пока все строки не будут прочитаны, или вы можете прочитать все из файла за один раз.Существуют способы чтения различного количества байтов, но это немного сложнее и выходит за рамки этого процесса проектирования.То, что я обычно делаю, это;Я прочитаю содержимое файла и сохраню его в виде строки или набора строк.Затем, после того как я получил информацию из файла;Затем я могу закрыть его и покончить с этим.Как только я сохраню эту информацию;затем я буду анализировать строковые данные, а затем я буду заполнять свои структуры данных анализируемыми данными.Мне нравится разбивать вещи на отдельные функции, чтобы разделить их логику и ответственность.
Ваша структура кода может выглядеть примерно так:
struct MyDataType {
// the contents that you will store from a file.
};
// A basic method to split a string based on a single delimiter
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;
}
// Similar to above but with the ability to use a string as a delimiter as opposed to just a single char
std::vector<std::string> splitString( const std::string& strStringToSplit, const std::string& strDelimiter, const bool keepEmpty = true ) {
std::vector<std::string> tokens;
if( strDelimiter.empty() ) {
tokens.push_back( strStringToSplit );
return tokens;
}
std::string::const_iterator itSubStrStart = strStringToSplit.begin(), itSubStrEnd;
while( true ) {
itSubStrEnd = search( itSubStrStart, strStringToSplit.end(), strDelimiter.begin(), strDelimiter.end() );
std::string strTemp( itSubStrStart, itSubStrEnd );
if( keepEmpty || !strTemp.empty() ) {
tokens.push_back( strTemp );
}
if( itSubStrEnd == strStringToSplit.end() ) {
break;
}
itSubStrStart = itSubStrEnd + strDelimiter.size();
}
return tokens;
}
// This function will open a file, read a single line
// closes the file handle and returns that line as a std::string
std::string getLineFromFile( const char* filename ) {
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;
std::getline( file, line );
file.close();
return line;
}
// This function will open a file and read the file line by line
// storing each line as a string and closes the file then returns
// the contents as a std::vector<std::string>
void getAllLinesFromFile( 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();
}
// This function will open a file and read all of the file's contents and store it into
// a large buffer or a single string.
void getDataFromFile( const char* filename, 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::stringstream buf;
buf << file.rdbuf();
output.clear();
output.reserve( buf.str().length() );
output = buf.str();
}
// The declaration of this can vary too; depending on if you are doing a single line
// from the file, doing single line at a time for the entire file, or reading all
// of the contents from a large buffer.
void parseDataFromFile( const std::string& fileContents, std::vector<std::string>& output, std::vector<MyDataStructure>& data ) {
// This will vary on the file's data structure,
// but this is where you will call either of the `splitString` functions
// to tokenize the data.
// You will also use the `std::string's` conversion utilities such as
// std::stoi(...)... to convert to your basic types
// then you will create an instance of your data type structure
// and push that into the vector passed in.
}
Тогда ваш main будет выглядеть примерно так: I 'будем использовать line by line version
int main() {
try {
std::string fileContents;
getAllinesFromFile( "test.txt", fileContents );
std::vector<std::string> tokens;
std::vector<MyDataStructure> data;
parseDataFromFile( fileContents, tokens, data );
} catch( std::runtime_error& e ) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Это позволяет коду быть читаемым, более модульным, многократно используемым, а в некоторых случаях даже универсальным.Это также помогает минимизировать объем отладки и уменьшает управление кодом.
-Note- Также, если вы внимательно посмотрите на мои функции, где я читаю данные из файла;вы не увидите while( !file.eof() )
!Это плохая практика кода!Лучше всего использовать операторы std::getline(...)
или stream <<
в цикле while для чтения данных.