Ani*_*mar 5 c++ oop file-handling
我在文本文件中有以下格式的数据.文件名 - empdata.txt 请注意,行之间没有空格.
SL |雇员|名称|部|带|位置
1 | 327427 | Brock Mcneil |研究与开发| U2 | Pune
2 | 310456 | Acton Golden |广告| P3 |海德拉巴
3 | 305540 | Hollee Camacho |薪资| U3 |班加罗尔
4 | 218801 | Simone Myers |公共关系| U3 |浦那
5 | 144051 | Eaton Benson |广告| P1 | Chennai
我有这样的课
class empdata
{
public:
int sl,empNO;
char name[20],department[20],band[3],location[20];
};
Run Code Online (Sandbox Code Playgroud)
我创建了一个empdata类的对象数组.如何从具有上述指定格式的n行数据的文件中读取数据并将它们存储到创建的(类)对象数组中?
这是我的代码
int main () {
string line;
ifstream myfile ("empdata.txt");
for(int i=0;i<10;i++) //processing only first 10 lines of the file
{
getline (myfile,line);
//What should I do with this "line" so that I can extract data
//from this line and store it in the class object?
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
所以基本上我的问题是如何从一个字符串中提取数据,该字符串的数据用'|'分隔 字符并将每个数据存储到单独的变量中
我更喜欢使用String Toolkit.String Toolkit将在分析时转换数字.
以下是我将如何解决它.
#include <fstream>
#include <strtk.hpp> // http://www.partow.net/programming/strtk
using namespace std;
// using strings instead of character arrays
class Employee
{
public:
int index;
int employee_number;
std::string name;
std::string department;
std::string band;
std::string location;
};
std::string filename("empdata.txt");
// assuming the file is text
std::fstream fs;
fs.open(filename.c_str(), std::ios::in);
if(fs.fail()) return false;
const char *whitespace = " \t\r\n\f";
const char *delimiter = "|";
std::vector<Employee> employee_data;
// process each line in turn
while( std::getline(fs, line ) )
{
// removing leading and trailing whitespace
// can prevent parsing problemsfrom different line endings.
strtk::remove_leading_trailing(whitespace, line);
// strtk::parse combines multiple delimeters in these cases
Employee e;
if( strtk::parse(line, delimiter, e.index, e.employee_number, e.name, e.department, e.band, e.location) )
{
std::cout << "succeed" << std::endl;
employee_data.push_back( e );
}
}
Run Code Online (Sandbox Code Playgroud)
AFAIK,没有任何东西可以开箱即用。但您拥有自己构建它的所有工具
您将这些行读入 char * (带有cin.getline()),然后使用 strtok 和 strcpy
该getline函数接受第三个参数来指定分隔符。您可以利用它通过istringstream. 就像是 :
int main() {
std::string line, temp;
std::ifstream myfile("file.txt");
std::getline(myfile, line);
while (myfile.good()) {
empdata data;
std::getline(myfile, line);
if (myfile.eof()) {
break;
}
std::istringstream istr(line);
std::getline(istr, temp, '|');
data.sl = ::strtol(temp.c_str(), NULL, 10);
std::getline(istr, temp, '|');
data.empNO = ::strtol(temp.c_str(), NULL, 10);
istr.getline(data.name, sizeof(data.name), '|');
istr.getline(data.department, sizeof(data.department), '|');
istr.getline(data.band, sizeof(data.band), '|');
istr.getline(data.location, sizeof(data.location), '|');
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这是上一个的 C++ 版本
您将这些行读入字符串(就像您当前所做的那样)并用于string::find(char sep, size_t pos)查找分隔符的下一个出现,并将子字符串开头和分隔符之间的数据(来自 string::c_str())复制到您的字段
您只需迭代该字符串即可。如果字符是分隔符,则在当前字段的末尾放置 NULL 并传递到下一个字段。否则,您只需将字符写入当前字段的当前位置即可。
如果您更习惯其中一种,请坚持使用。
以下只是我的看法。
这种getline方式将是最简单的编码和维护方式。
找到的路是中级的。它仍然处于相当高的水平并且避免使用istringstream.
手动方式的级别非常低,因此您应该构建它以使其可维护。例如,您可以将行明确描述为具有最大大小和当前位置的字段数组。由于你同时拥有 int 和 char[] 字段,这会很棘手。但您可以按照您想要的方式轻松配置它。例如,您的代码只允许department字段包含 20 个字符,而Research and Development第 2 行则更长。如果不进行特殊处理,getline 方式将使 处于istringstream错误状态,并且不会再读取任何内容。即使你清除了状态,你的处境也会很糟糕。因此,您应该首先读入 a std::string,然后将开头复制到该char *字段。
这是一个工作手册实施:
class Field {
public:
virtual void reset() = 0;
virtual void add(empdata& data, char c) = 0;
};
class IField: public Field {
private:
int (empdata::*data_field);
bool ok;
public:
IField(int (empdata::*field)): data_field(field) {
ok = true;
reset();
}
void reset() { ok = true; }
void add(empdata& data, char c);
};
void IField::add(empdata& data, char c) {
if (ok) {
if ((c >= '0') && (c <= '9')) {
data.*data_field = data.*data_field * 10 + (c - '0');
}
else {
ok = false;
}
}
}
class CField: public Field {
private:
char (empdata::*data_field);
size_t current_pos;
size_t size;
public:
CField(char (empdata::*field), size_t size): data_field(field), size(size) {
reset();
}
void reset() { current_pos = 0; }
void add(empdata& data, char c);
};
void CField::add(empdata& data, char c) {
if (current_pos < size) {
char *ix = &(data.*data_field);
ix[current_pos ++] = c;
if (current_pos == size) {
ix[size -1] = '\0';
current_pos +=1;
}
}
}
int main() {
std::string line, temp;
std::ifstream myfile("file.txt");
Field* fields[] = {
new IField(&empdata::sl),
new IField(&empdata::empNO),
new CField(reinterpret_cast<char empdata::*>(&empdata::name), 20),
new CField(reinterpret_cast<char empdata::*>(&empdata::department), 20),
new CField(reinterpret_cast<char empdata::*>(&empdata::band), 3),
new CField(reinterpret_cast<char empdata::*>(&empdata::location), 20),
NULL
};
std::getline(myfile, line);
while (myfile.good()) {
Field** f = fields;
empdata data = {0};
std::getline(myfile, line);
if (myfile.eof()) {
break;
}
for (std::string::const_iterator it = line.begin(); it != line.end(); it++) {
char c;
c = *it;
if (c == '|') {
f += 1;
if (*f == NULL) {
continue;
}
(*f)->reset();
}
else {
(*f)->add(data, c);
}
}
// do something with data ...
}
for(Field** f = fields; *f != NULL; f++) {
free(*f);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
它直接健壮、高效且可维护:添加字段很容易,并且可以容忍输入文件中的错误。但它比其他的要慢得多,并且需要更多的测试。因此,如果没有特殊原因,我不建议使用它(需要接受多个分隔符、可选字段和动态顺序,...)