在 C++ 中读取文本文件的特定部分
Reading a specific part of a text file in C++
我正在读取带有 <fstream>
的文件,并且该文件包含如下行:
Name=Cecil
Age=34
Born=London
//etc.
为了示例,假设我想将 34
分配给变量 int age
。这意味着我必须找到哪一行以 Age
开头,然后提取等号后的值 34
。
稍后我想在程序中改变这个值,并在文件中替换它,所以 34
会变成例如 35
,所以再次,我必须找到这个特定的文件的一部分并写在那里。
这似乎是一项基本任务,但到目前为止我找不到任何好的例子。也许将我的数据存储为 JSON 或 XML 并为此目的使用库会更好?
这是基本需求,但不是基本实现,因为文件只是字节流,没有结构。这意味着您需要对写入的数据实施某种算法 interpret/parse 。您应该知道您不能删除文件中的内容,也不能插入;我的意思是没有单一的操作可以解决问题,这在您的情况下是个问题,因为如果您想用 7 替换 34,则该信息的长度不同并且您想删除一个字符(不可能)并替换另一个(这个有可能);将 34 替换为 103 时也是同样的道理。这就是数据库的用途:让您轻松管理结构化信息。
好的,这并不意味着这是不可能的,因为数据库使用文件。可能是,初步估计您可以:读取 all 文件并将其解析为内存中的结构化数据,然后管理该数据并在需要时将数据写入新文件。当然,如果你有太多数据,这不是一个认真的实现。
由于您的数据看起来像键值对,因此这是一种面向对象的方法:
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
struct DATA
{
struct PAIR
{
string m_szComponentSeparator;
string m_szKey;
string m_szValue;
PAIR()
{
m_szComponentSeparator = "=";
}
void Read(ifstream &_inFile)
{
string szLine;
_inFile >> szLine;
size_t pos = szLine.find(m_szComponentSeparator);
m_szKey = szLine.substr(0, pos);
m_szValue = szLine.substr(pos + 1);
}
void Write(ofstream &_outFile)
{
_outFile << m_szKey << m_szComponentSeparator << m_szValue << endl;
}
int To_int()
{
return atoi(m_szValue.c_str());
}
void SetAs_int(int _iValue)
{
char szBuf[32] = {0};
snprintf(szBuf, sizeof(szBuf), "%d", _iValue);
m_szValue = szBuf;
}
};
PAIR m_stName;
PAIR m_stAge;
PAIR m_stBorn;
void Read(string _szFilepath)
{
ifstream inFile(_szFilepath.c_str());
m_stName.Read(inFile);
m_stAge.Read(inFile);
m_stBorn.Read(inFile);
}
void Write(string _szFilepath)
{
ofstream outFile(_szFilepath.c_str());
m_stName.Write(outFile);
m_stAge.Write(outFile);
m_stBorn.Write(outFile);
}
void Print()
{
cout << m_stName.m_szKey << m_stName.m_szComponentSeparator << m_stName.m_szValue << endl;
cout << m_stAge.m_szKey << m_stAge.m_szComponentSeparator << m_stAge.m_szValue << endl;
cout << m_stBorn.m_szKey << m_stBorn.m_szComponentSeparator << m_stBorn.m_szValue << endl;
}
};
int main()
{
string szInputFile = "in.txt";
string szOutputFile = "out.txt";
DATA oData;
oData.Read(szInputFile);
oData.Print();
int age = oData.m_stAge.To_int();
age = 35;
oData.m_stAge.SetAs_int(age);
cout << "after modification:" << endl;
oData.Print();
oData.Write(szOutputFile);
return 0;
}
//Output:
Name=Cecil
Age=34
Born=London
after modification
Name=Cecil
Age=35
Born=London
这只是一些一般性的推理,作为对最终答案的回答 "Maybe it would be better to ...":
任何简单的实现都会有点脆弱,但如果您对输入文件有很好的控制,并且可能的数据损坏风险不会太困扰您(即损坏会很低且容易restore/repair),即使进行字符串替换,搜索 "Age=34\n" 并将其替换为 "Age=35\n"(如果文件中只有一条年龄线,否则您需要先找到正确的人),您也可能逃脱。 ..首先将整个文件读入内存,然后在替换后将其写回。
选择 JSON 将使您在结构上更加安全(不会意外更改 "CarnAge=34" 而不是 "Age=34"),但 JSON 仍然是可编辑的在纯文本编辑器中(如果你足够小心的话)。 JSON 也是与 Web 服务交换数据的一种非常流行的方式。本段也适用于 XML,尽管就纯文本编辑而言,我认为 JSON 对于非程序员来说更容易学习。所以 "ini" 可以,但我还是更喜欢 JSON.
最后,如果这更严重,考虑一些数据库将很有意义。这些是经过验证的解决方案,可以非常有效地 store/modify/fetch 数据,文件 I/O 比天真的 read/write 整个文件更有效(数据库正在处理文件,允许他们通过仅覆盖来修改内容它的一部分,通过保留保留 space 并具有完全重组某些文件的一部分的机制)。此外,大多数数据库都是 ACID,因此可以为您的应用增加额外的稳健性 "for free"。您只是无法编辑纯文本数据(但您仍然可以在某些 sheet 中准备数据,并通过某些数据库管理器工具导入它们)。
我能想到的避免使用某些常见数据库的唯一原因是:1) 性能(在特殊情况下,优秀的程序员会胜过专业的数据库引擎,但大多数时候不值得付出巨大的努力), 2) 项目太 small/tutorial,所以虽然集成例如 "sqlite" 并不难,但读取整个文件并替换字符串就更简单了。但是例如我用 U++ 框架编写我的 C++ 项目,所以添加 SQL 将是几行额外的轻而易举的事,没有大麻烦...
试试这个
ifstream ifs("e:\file.txt");
vector<string> file;
string temp;
while(getline(ifs, temp))
{
if(temp.compare(0,4,"Age=")==0)
temp.replace(0, sizeof("Age=125"),"Age=125");
file.push_back(temp);
}
ifs.close();
ofstream ofs("e:\file.txt");
for(auto begin = file.begin(), end = file.end(); begin!=end; begin++)
ofs << *begin << endl;
ofs.close();
我正在读取带有 <fstream>
的文件,并且该文件包含如下行:
Name=Cecil
Age=34
Born=London
//etc.
为了示例,假设我想将 34
分配给变量 int age
。这意味着我必须找到哪一行以 Age
开头,然后提取等号后的值 34
。
稍后我想在程序中改变这个值,并在文件中替换它,所以 34
会变成例如 35
,所以再次,我必须找到这个特定的文件的一部分并写在那里。
这似乎是一项基本任务,但到目前为止我找不到任何好的例子。也许将我的数据存储为 JSON 或 XML 并为此目的使用库会更好?
这是基本需求,但不是基本实现,因为文件只是字节流,没有结构。这意味着您需要对写入的数据实施某种算法 interpret/parse 。您应该知道您不能删除文件中的内容,也不能插入;我的意思是没有单一的操作可以解决问题,这在您的情况下是个问题,因为如果您想用 7 替换 34,则该信息的长度不同并且您想删除一个字符(不可能)并替换另一个(这个有可能);将 34 替换为 103 时也是同样的道理。这就是数据库的用途:让您轻松管理结构化信息。
好的,这并不意味着这是不可能的,因为数据库使用文件。可能是,初步估计您可以:读取 all 文件并将其解析为内存中的结构化数据,然后管理该数据并在需要时将数据写入新文件。当然,如果你有太多数据,这不是一个认真的实现。
由于您的数据看起来像键值对,因此这是一种面向对象的方法:
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
struct DATA
{
struct PAIR
{
string m_szComponentSeparator;
string m_szKey;
string m_szValue;
PAIR()
{
m_szComponentSeparator = "=";
}
void Read(ifstream &_inFile)
{
string szLine;
_inFile >> szLine;
size_t pos = szLine.find(m_szComponentSeparator);
m_szKey = szLine.substr(0, pos);
m_szValue = szLine.substr(pos + 1);
}
void Write(ofstream &_outFile)
{
_outFile << m_szKey << m_szComponentSeparator << m_szValue << endl;
}
int To_int()
{
return atoi(m_szValue.c_str());
}
void SetAs_int(int _iValue)
{
char szBuf[32] = {0};
snprintf(szBuf, sizeof(szBuf), "%d", _iValue);
m_szValue = szBuf;
}
};
PAIR m_stName;
PAIR m_stAge;
PAIR m_stBorn;
void Read(string _szFilepath)
{
ifstream inFile(_szFilepath.c_str());
m_stName.Read(inFile);
m_stAge.Read(inFile);
m_stBorn.Read(inFile);
}
void Write(string _szFilepath)
{
ofstream outFile(_szFilepath.c_str());
m_stName.Write(outFile);
m_stAge.Write(outFile);
m_stBorn.Write(outFile);
}
void Print()
{
cout << m_stName.m_szKey << m_stName.m_szComponentSeparator << m_stName.m_szValue << endl;
cout << m_stAge.m_szKey << m_stAge.m_szComponentSeparator << m_stAge.m_szValue << endl;
cout << m_stBorn.m_szKey << m_stBorn.m_szComponentSeparator << m_stBorn.m_szValue << endl;
}
};
int main()
{
string szInputFile = "in.txt";
string szOutputFile = "out.txt";
DATA oData;
oData.Read(szInputFile);
oData.Print();
int age = oData.m_stAge.To_int();
age = 35;
oData.m_stAge.SetAs_int(age);
cout << "after modification:" << endl;
oData.Print();
oData.Write(szOutputFile);
return 0;
}
//Output:
Name=Cecil
Age=34
Born=London
after modification
Name=Cecil
Age=35
Born=London
这只是一些一般性的推理,作为对最终答案的回答 "Maybe it would be better to ...":
任何简单的实现都会有点脆弱,但如果您对输入文件有很好的控制,并且可能的数据损坏风险不会太困扰您(即损坏会很低且容易restore/repair),即使进行字符串替换,搜索 "Age=34\n" 并将其替换为 "Age=35\n"(如果文件中只有一条年龄线,否则您需要先找到正确的人),您也可能逃脱。 ..首先将整个文件读入内存,然后在替换后将其写回。
选择 JSON 将使您在结构上更加安全(不会意外更改 "CarnAge=34" 而不是 "Age=34"),但 JSON 仍然是可编辑的在纯文本编辑器中(如果你足够小心的话)。 JSON 也是与 Web 服务交换数据的一种非常流行的方式。本段也适用于 XML,尽管就纯文本编辑而言,我认为 JSON 对于非程序员来说更容易学习。所以 "ini" 可以,但我还是更喜欢 JSON.
最后,如果这更严重,考虑一些数据库将很有意义。这些是经过验证的解决方案,可以非常有效地 store/modify/fetch 数据,文件 I/O 比天真的 read/write 整个文件更有效(数据库正在处理文件,允许他们通过仅覆盖来修改内容它的一部分,通过保留保留 space 并具有完全重组某些文件的一部分的机制)。此外,大多数数据库都是 ACID,因此可以为您的应用增加额外的稳健性 "for free"。您只是无法编辑纯文本数据(但您仍然可以在某些 sheet 中准备数据,并通过某些数据库管理器工具导入它们)。
我能想到的避免使用某些常见数据库的唯一原因是:1) 性能(在特殊情况下,优秀的程序员会胜过专业的数据库引擎,但大多数时候不值得付出巨大的努力), 2) 项目太 small/tutorial,所以虽然集成例如 "sqlite" 并不难,但读取整个文件并替换字符串就更简单了。但是例如我用 U++ 框架编写我的 C++ 项目,所以添加 SQL 将是几行额外的轻而易举的事,没有大麻烦...
试试这个
ifstream ifs("e:\file.txt");
vector<string> file;
string temp;
while(getline(ifs, temp))
{
if(temp.compare(0,4,"Age=")==0)
temp.replace(0, sizeof("Age=125"),"Age=125");
file.push_back(temp);
}
ifs.close();
ofstream ofs("e:\file.txt");
for(auto begin = file.begin(), end = file.end(); begin!=end; begin++)
ofs << *begin << endl;
ofs.close();