如何从包含以"|"分隔的字段的行中提取数据 C++中的字符?

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)

所以基本上我的问题是如何从一个字符串中提取数据,该字符串的数据用'|'分隔 字符并将每个数据存储到单独的变量中

Dan*_*nyK 7

我更喜欢使用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)


Ser*_*sta 4

AFAIK,没有任何东西可以开箱即用。但您拥有自己构建它的所有工具

C方式

您将这些行读入 char * (带有cin.getline()),然后使用 strtok 和 strcpy

getline方式

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)

它直接健壮、高效且可维护:添加字段很容易,并且可以容忍输入文件中的错误。但它比其他的要慢得多,并且需要更多的测试。因此,如果没有特殊原因,我不建议使用它(需要接受多个分隔符、可选字段和动态顺序,...)