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

Posted

技术标签:

【中文标题】如何从具有由“|”分隔的字段的行中提取数据C++中的字符?【英文标题】:How to extract data from a line which has fields separated by '|' character in C++? 【发布时间】:2015-10-17 11:03:51 【问题描述】:

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

Sl|员工ID|姓名|部门|乐队|位置

1|327427|Brock Mcneil|研发|U2|浦那

2|310456|Acton Golden|广告|P3|海得拉巴

3|305540|霍莉·卡马乔|工资单|U3|班加罗尔

4|218801|西蒙娜·迈尔斯|公共关系|U3|浦那

5|144051|伊顿本森|广告|P1|钦奈

我有这样的课

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;

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

【问题讨论】:

到目前为止你有什么尝试, 我需要一个关于如何进行的想法。一直试图弄清楚几个小时。请帮忙 我已经用我所做的更新了这个问题 看来你在用 C 试试,我用 JAVA 举例,然后需要再举一个例子 @AnishKumar 您是否还必须考虑丢失数据,或者我们在这里谈论的是完美的数据集? 【参考方案1】:

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

数据:由您提供:在名为 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

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

【讨论】:

基本上我的问题是如何从数据由'|'分隔的字符串中提取数据字符并将每个数据存储到单独的变量中。我在 C 中做这件事 @Forhad:既然 OP 现在已经发布了代码,很明显他正在使用 C++。所以你可能应该调整你的答案,或者删除它,因为 C++ 问题的 Java 答案看起来有点不合适。 ;-) 是时候使用 c++ @DevSolar 获得另一个答案了【参考方案2】:

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;

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

【讨论】:

【参考方案3】:

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


【讨论】:

【参考方案4】:

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

【讨论】:

以上是关于如何从具有由“|”分隔的字段的行中提取数据C++中的字符?的主要内容,如果未能解决你的问题,请参考以下文章

如何从分隔类型文件中的特定行打印特定字段

如何转换由 | 分隔的顺序数据并且在 pyspark 中的行和列中没有换行符

如何在javascript中使用从数据库返回的行中的数据

从Excel中的行中提取唯一值

@mysql/xdevapi 如何在nodejs中调试“插入的行中错误的字段数”?

正则表达式从带有空格分隔符和描述中的空格的行中读取发票行详细信息