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

时间:2015-07-27 09:56:42

标签: 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 | Pune

     

5 | 144051 | Eaton Benson |广告| P1 | Chennai

我有一个这样的课程

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

我创建了一个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;
}

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

4 个答案:

答案 0 :(得分:7)

我更喜欢使用String Toolkit。 String Toolkit将在解析数据时对其进行转换。

以下是我将如何解决它。

public class Client {
....

void sendGet(){

    final String request_line="GET /id http/1.1";
    PrintWriter out = null;
    .....
    try {
        out = new PrintWriter(ag_socket.getOutputStream(), true);
        // Send request to http-agent
        out.println(request_line);
        out.println();   // blank line separating header & body
    } catch (IOException e) {
        e.printStackTrace();
    }finally{
        //out.close();
    }
}

答案 1 :(得分: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;
}

这是前一个的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] = '\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;
}

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

答案 2 :(得分: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

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

答案 3 :(得分:0)

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