How to extract data from a row with fields separated by '|' character in c ++?

I have data in the following format in a text file. The file name is empdata.txt. Note that there is no space between the lines.

Sl | EmployeeID | Name | Department | Group | 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 | Simon Myers | Public Relations | U3 | Pune

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

I have a class like this

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

I created an array of objects of the empdata class. How to read data from a file with n lines of data in the above format and save them in an array of created objects (classes)?

This is my code.

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

So basically my question is how to extract data from a string that has data separated by '|' character and save each information in a separate variable

+5
source share
4 answers

I prefer to use the String Toolkit . The String Toolkit will take care of converting numbers when it is parsed.

This is how I solve it.

 #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 ); } } 
+7
source

AFAIK, there is nothing that would make it out of the box. But you have all the tools for creating it yourself.

Path C

You read the lines in char * (with cin.getline() ) and then use strtok and strcpy

Getline method

The getline function takes a third parameter to specify the delimiter. You can use this to split the line into istringstream . Sort of:

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

This is the C ++ version of the previous version.

Search method

You read the lines in the line (as you do now) and use string::find(char sep, size_t pos) to find the next origin of the delimiter and copy the data (from the string :: c_str ()) between the beginning of the substring and the delimiter to your fields

Manual way

You just iterate over the string. If the character is a delimiter, you put NULL at the end of the current field and move on to the next field. In addition, you simply write a character at the current position of the current field.

What to choose?

If you are more used to one of them, stick to it.

That's just my opinion.

The getline path will be the easiest to code and support.

The search method is medium. It is still at a fairly high level and avoids using istringstream .

The manual method will be really low, so you need to structure it to make it serviceable. For example, you may have an explicit description of the lines as an array of fields with the maximum size and current position. And since you have both int and char [], this will be tricky. But you can easily customize it the way you want. For example, your code only allows 20 characters for the department field, while Research and Development on line 2 is longer. Without special processing, the getline path will leave istringstream in a bad state and will not read anything else. And even if you clear the state, you will be in a bad mood. Therefore, you must first read std::string , and then copy the beginning to the char * field.

The following is a work instruction:

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

It is direct, reliable, efficient, and maintainable: adding a field is simple and tolerant of errors in the input file. But this is not like the others, and this will require much more tests. Therefore, I do not recommend using it without special reasons (the need to accept several separators, optional fields and a dynamic order, ...)

+4
source

Try this simple code segment, it will read the file and, giving a print, you can read it in turn, and later you can use this for processing as needed.

Data: provided that you: in a file named 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(); } } } 

In the console, you will get the result as follows:

 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 

This is a simple idea, you can do what you need.

0
source

In C ++, you can change the locale to add an extra character to the list of separators of the current language:

 #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"; } } 
0
source

All Articles