如何从字段由“|”分隔的行中提取数据C++中的字符?

How to extract data from a line which has fields separated by '|' character in C++?

我在文本文件中有以下格式的数据。 文件名 - empdata.txt 注意行与行之间没有空格space。

Sl|EmployeeID|Name|Department|Band|Location

1|327427|Brock Mcneil|Research and Development|U2|Pune

2|310456|Acton Golden|Advertising|P3|Hyderabad

3|305540|Hollee Camacho|Payroll|U3|Bangalore

4|218801|Simone Myers|Public Relations|U3|Pune

5|144051|Eaton Benson|Advertising|P1|Chennai

我有一个class这样的

class empdata
{
public:
int sl,empNO;
char name[20],department[20],band[3],location[20];
};

我创建了一个 class empdata 的对象数组。 如何从具有上述指定格式的n行数据的文件中读取数据并将它们存储到创建的(class)个对象数组中?

这是我的代码

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;
}

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

试试这个简单的代码段,这将读取文件并打印出来,您可以逐行读取,稍后您可以根据需要使用它进行处理。

数据:由您提供:在名为 data.txt.

的文件中
package com.demo;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;

public class Demo {

    public static void main(String a[]) {
        try {
            File file = new File("data.txt");
            FileReader fileReader = new FileReader(file);
            BufferedReader bufferReader = new BufferedReader(fileReader);
            String data;

            while ((data = bufferReader.readLine()) != null) {
                // data = br.readLine( );
                System.out.println(data);
            }   

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在控制台中你会得到这样的输出:

Sl|EmployeeID|Name|Department|Band|Location
1|327427|Brock Mcneil|Research and Development|U2|Pune
2|310456|Acton Golden|Advertising|P3|Hyderabad
3|305540|Hollee Camacho|Payroll|U3|Bangalore
4|218801|Simone Myers|Public Relations|U3|Pune
5|144051|Eaton Benson|Advertising|P1|Chennai

这是一个简单的想法,你可以做你需要的。

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;
}

这是上一个的C++版本

找到方法

你将这些行读入一个字符串(就像你现在所做的那样)并使用 string::find(char sep, size_t pos) 找到分隔符的下一个出现并复制数据(从 string::c_str() )到子字符串的开头和分隔符到您的字段

手动方式

你只是迭代字符串。如果字符是分隔符,则将 NULL 放在当前字段的末尾并传递给下一个字段。否则,您只需在当前字段的当前位置写入字符。

选择哪个?

如果你比较习惯其中一种,那就坚持吧。

以下仅代表个人意见。

getline 方式将是最简单的编码和维护方式。

找到的方法是中级。它仍然处于相当高的水平并且避免使用istringstream

手动方式真的很低级,所以你应该构建它以使其易于维护。例如,您可以将这些行明确描述为具有最大大小和当前位置的字段数组。由于您同时拥有 int 和 char[] 字段,因此这将很棘手。但是您可以按照自己的方式轻松配置它。例如,您的代码只允许 department 字段包含 20 个字符,而第 2 行中的 Research and Development 更长。如果不进行特殊处理,getline 方式将使 istringstream 处于错误状态并且不会再读取任何内容。而且即使你清除了状态,你的位置也会很糟糕。所以你应该先读入一个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] = '[=11=]';
            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;
}

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

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

}

在 C++ 中,您可以更改语言环境以将额外的字符添加到当前语言环境的分隔符列表中:

#include <locale>
#include <iostream>

struct pipe_is_space : std::ctype<char> {
  pipe_is_space() : std::ctype<char>(get_table()) {}
  static mask const* get_table()
  {
    static mask rc[table_size];
    rc['|'] = std::ctype_base::space;
    rc['\n'] = std::ctype_base::space;
    return &rc[0];
  }
};

int main() {
  using std::string;
  using std::cin;
  using std::locale;

  cin.imbue(locale(cin.getloc(), new pipe_is_space));

  string word;
  while(cin >> word) {
    std::cout << word << "\n";
  }
}