I have a .txt parameter file like this:
#Stage
filename = "a.txt";
...
#Stage
filename = "b.txt";
...
Basically I want to read one stage each time I access the parameter file.
I planed to use getline in C++ with delimiter "#Stage" to do this. Or there is a better way to solve this? Any sample codes will be helpful.
*struct content{
DATA data;
content* next;
};
struct List{
content * head;
};
static List * list;
char buf[100];
FILE * f = fopen(filename);
while(NULL != fgets(buf,f))
{
char str[100] ={0};
sccanf(buf,"%s",str);
if(strcmp("#Stage",str) == 0)
{
// read content and add to list
cnntent * p = new content();
list->add();
}
else
{
//update content in last node
list->last().data =
}
}*
Maybe I should express more clear. Anyway, I manage like this:
ifstream file1;
file1.open(parfile, ios::binary);
if (!file1) {
cout<<"Error opening file"<<parfile<<"for read"<<endl;
exit(1);
}
std::istreambuf_iterator<char> eos;
std::string s(std::istreambuf_iterator<char>(file1), eos);
unsigned int block_begin = 0;
unsigned int block_end = string::npos;
for (unsigned int i=0; i<stage; i++) {
if(s.find("#STAGE", block_begin)!=string::npos) {
block_begin = s.find("#STAGE", block_begin);
}
}
if(s.find("#STAGE", block_begin)!=string::npos) {
block_end = s.find("#STAGE", block_begin);
}
string block = s.substr(block_begin, block_end);
stringstream ss(block);
....
I'd read line by line, ignoring the lines, starting with # (or the lines, with content #Stage, depending on the format/goal) (as there's no getline version, taking std::string as delimiter).
Related
I am currently doing an assignment in C++ in which I have to load a bunch of data from a .txt file into a linked list:
Here is my load code:
void record::load( string fileName ) throw( recordException )
{
ifstream inFile( fileName.c_str() );
string c;
string t;
double p;
int q;
node *tail;
while (!isEmpty())
{
remove( 1 );
}
size = 0;
if ( getline(inFile, c) ||getline(inFile,t)||inFile >> p>> q) // Is file empty?
{ // File not empty:
try
{
head = new node;
// Add the first integer to the list.
head->cat = c;
head->title = t;
head->price = p;
head->qty = q;
head->next = NULL;
tail = head;
size = size + 1;
inFile.skipws;
// Add remaining items to linked list.
while ( getline(inFile, c)||getline(inFile ,t)||inFile >> p>> q)
// while(inFile.getline(c,50)||inFile.getline(t,50)||inFile>>p>>q)
{
tail->next = new node;
tail = tail->next;
tail->cat = c;
tail->title = t;
tail->price = p;
tail->qty = q;
tail->next = NULL;
size = size + 1;
inFile.skipws;
} // end while
} // end try
catch (bad_alloc e)
{
throw recordException(
"recordException: restore cannot allocate memory.");
} // end catch
} // end if
inFile.close();
}
It works great, except for one issue: I get really weird numbers when I try to display the data that is loaded in my linked list.
This is what it looks like when my program runs:
Can anyone help me? What can I do to remove those numbers? Why do these numbers occur anyway?
I am using C++ Codeblocks 16.01, if that is of any help.
Next time, don't use images, always copy/paste the actual text into your post instead.
Your reading code does not match the format of your input data. When you call getline(), it reads until it encounters a line break or EOF.
So, the first call to getline(inFile, c) reads the ENTIRE first line in one go:
" DVD Snow White 20 20"
Then getline(inFile,t) reads the NEXT line in one go:
" VCD Cinderella 10 10"
Then inFile >> p >> q tries to parse the NEXT line and fails:
" MT Wonderworld 20 30"
Since MT is not a double and Wonderworld is not an int.
You need to read the file line-by-line, parsing each line individually, for example:
#include <string>
#include <sstream>
void record::load( std::string fileName ) throw( recordException )
{
while (!isEmpty())
remove( 1 );
size = 0;
std::ifstream inFile( fileName.c_str() );
if (!inFile.is_open())
return;
std::string line;
std::string c;
char t[25+1] = {0}; // <-- use whatever your max title length actually is...
double p;
int q;
node *newNode;
while (std::getline(inFile, line)) // Is file empty?
{
std::istringstream iss(line);
if ((iss >> c) && iss.get(t, sizeof(t)) && (iss >> p >> qty))
{
try
{
newNode = new node;
newNode->cat = c;
newNode->title = t;
newNode->price = p;
newNode->qty = q;
newNode->next = NULL;
if (!head)
head = newNode;
if (tail)
tail->next = newNode;
tail = newNode;
++size;
}
catch (const bad_alloc &e)
{
throw recordException(
"recordException: restore cannot allocate memory.");
}
}
}
}
Regular Expression is much more suitable for task like this. You can use something like:
std::string test = " DVD Snow White 20 20";
std::regex re("\\s*(\\S+)\\s+(\\S.*\\S)\\s+(\\d+)\\s+(\\d+)");
std::smatch match;
if (std::regex_match(test, match, re)) {
for (size_t i = 0; i < match.size(); ++i) {
std::cout << "submatch " << i << ": \"" << match[i] << "\"\n";
}
}
live example
So you could read file line by line and split it using that RE:
std::regex re("\\s*(\\S+)\\s+(\\S.*\\S)\\s+(\\d+)\\s+(\\d+)");
std::string line;
while( std::getline( inFile, line ) ) {
std::smatch match;
if (!std::regex_match(test, match, re)) continue;
std::string cat = match[1].str();
std::string title = match[2].str();
double price = std::stod( match[3].str() );
int qty = std::stoi( match[4].str() );
// you insert your node into list here
}
Note: I made RE pattern based on your input - price contains only digits, no dot. If it is not the case you would need to modify RE accordingly.
I have data in the following format in a text file. Filename - empdata.txt
Note that there are no blank space between the lines.
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
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 class empdata.
How to read the data from the file which has n lines of data in the above specified format and store them to the array of (class)objects created?
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 which has data separated by '|' character and store each data to a separate variable
I prefer to use the String Toolkit. The String Toolkit will take care of converting the numbers as it parses.
Here is how I would 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 );
}
}
AFAIK, there is nothing that does it out of the box. But you have all the tools to build it yourself
The C way
You read the lines into a char * (with cin.getline()) and then use strtok, and strcpy
The getline way
The getline function accept a third parameter to specify a delimiter. You can make use of that to split the line through a istringstream. Something like :
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 one
The find way
You read the lines into a string (as you currently do) and use string::find(char sep, size_t pos) to find next occurence of the separator and copy the data (from string::c_str()) between start of substring and separator to your fields
The manual way
You just iterate the string. If the character is a separator, you put a NULL at the end of current field and pass to next field. Else, you just write the character in current position of current field.
Which to choose ?
If you are more used to one of them, stick to it.
Following is just my opinion.
The getline way will be the simplest to code and to maintain.
The find way is mid level. It is still at a rather high level and avoids the usage of istringstream.
The manual way will be really low level, so you should structure it to make it maintainable. For example your could a explicit description of the lines as an array of fields with a maximimum size and current position. And as you have both int and char[] fields it will be tricky. But you can easily configure it the way you want. For example, your code only allow 20 characters for department field, whereas Research and Development in line 2 is longer. Without special processing, the getline way will leave the istringstream in bad state and will not read anything more. And even if you clear the state, you will be badly positionned. So you should first read into a std::string and then copy the beginning to the char * field.
Here is a working manual implementation :
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 directly robust, efficient and maintainable : adding a field is easy, and it is tolerant to errors in input file. But it is way loooonger than the other ones, and would need much more tests. So I would not advise to use it without special reasons (necessity to accept multiple separators, optional fields and dynamic order, ...)
Try this simple code segment , this will read the file and , give a print , you can read line by line and later you can use that to process as you need .
Data : provided bu you : in 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 console you will get output like this :
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 may do what you need.
In C++ you can change the locale to add an extra character to the separator list of the current locale:
#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";
}
}
I'm using libzip to extract the content of each file in a zip into my own data structure, a C++ immutable POD.
The problem is that every time I extract the content of a file, I get some random data with tacked on to the end. Here's my code:
void Parser::populateFileMetadata() {
int error = 0;
zip *zip = zip_open(this->file_path.c_str(), 0, &error);
if (zip == nullptr) {
LOG(DEBUG)<< "Could not open zip file.";
return;
}
const zip_int64_t n_entries = zip_get_num_entries(zip, ZIP_FL_UNCHANGED);
for (zip_int64_t i = 0; i < n_entries; i++) {
const char *file_name = zip_get_name(zip, i, ZIP_FL_ENC_GUESS);
struct zip_stat st;
zip_stat_init(&st);
zip_stat(zip, file_name, (ZIP_FL_NOCASE|ZIP_FL_UNCHANGED), &st);
char *content = new char[st.size];
zip_file *file = zip_fopen(zip, file_name, \
(ZIP_FL_NOCASE|ZIP_FL_UNCHANGED));
const zip_int64_t did_read = zip_fread(file, content, st.size);
if (did_read <= 0) {
LOG(WARNING)<< "Could not read contents of " << file_name << ".";
continue;
}
const FileMetadata metadata(string(file_name), -1, string(content));
this->file_metadata.push_back(metadata);
zip_fclose(file);
delete[] content;
}
zip_close(zip);
}
You're constructing a std::string from content without telling the constructor how long it is, so the constructor is going to read from the start of the buffer until it finds a terminating NUL. But there's no guarantee that the file contains one, and so the constructor reads past the end of your buffer until it happens to find a NUL.
Fix: use the two-argument std::string constructor (string(const char* s, size_t size)) and pass it the data length.
zip_fread seems to increase the size of content, so I just truncate content: content[st.size] = '\0';
#ruipacheco solution did not work for me. Doing content[st.size] = '\0'; fixed the problem but caused the error "double free or corruption..." when calling zip_fclose() and/or delete[] content So I did the below and it seems to work
void ReadZip(std::string &data){
....
....
data.resize(st.size);
for(uint i = 0; i<st.size; ++i)
data[i] = std::move(content[i]);
}
I have three string file that will be stored into dynamic array, but I just try one of three file to test if this succed, so I'll do the same way to handle the three file i have.
the goal i'll shown the string that I get from the file to a ListView
this my code.
void __fastcall TFrmNewPeta::showDefaultRute() {
std::string lineDataAwal;
std::ifstream ifs_Awal;
int tempIndexAwal = 0;
ifs_Awal.open("DefaultDataAwal");
/*counting the line*/
while(std::getline(ifs_Awal,lineDataAwal)){++tempIndexAwal;}
/*use dynamic array to stored string*/
std::string *s = new std::string[tempIndexAwal];
for(int dx=0;dx<tempIndexAwal;dx++)
{
while(std::getline(ifs_Awal,lineDataAwal))
s[dx] = lineDataAwal[dx++];
}
for(int dex =0;dex<tempIndexAwal;++dex)
{
ItemDefult = ListView1->Items->Add();
ItemDefult->Caption = String(IntToStr(dex + 1));
ItemDefult->SubItems->Add(s[dex].c_str());
}
ifs_Awal.close();
delete []s;
s = NULL;
}
there's no errors during compile, but the result ListView just showing the number with this code ItemDefult->Caption = String(IntToStr(dex + 1));
can anyone show me how the best way for i do.
You are reading the file, leaving it open, and expecting to read it again. That won't work because the cursor in the file is at the end of the file (so your second while loop does nothing).
A much better approach would be:
std::vector<std::string> lines;
std::string line;
std::ifstream fin("Youfilename");
while (std::getline(fin, line))
{
lines.push_back(line);
}
fin.close();
// add data to your list view
its easier if you use std::vector for dynamic arrays and don't forget to first include the file header with #include<vector>
void __fastcall TFrmNewPeta::showDefaultRute() {
std::string lineDataAwal;
std::ifstream ifs_Awal;
std::vector<std::string> vec;
ifs_Awal.open("DefaultDataAwal");
/*get the string of lineDataAwal */
while(std::getline(ifs_Awal,lineDataAwal))
{ vec.push_back(lineDataAwal);}
for(int dex =0;dex<vec.size();++dex)
{
ItemDefult = ListView1->Items->Add();
ItemDefult->Caption = String(IntToStr(dex + 1));
ItemDefult->SubItems->Add(vec.at(dex).c_str());
}
ifs_Awal.close();
}
Hope this helps
Im having a hard time understanding how to read the first line of a file. im trying to read the 1st line of a file and then check to see if its blank. this is what i came up with but still not working
void buildTree( NodePtr &root, ifstream& input )
{
char line [50];
line= input.getline();
if ( line == NULL )
{
root = NULL;
return;
}
}
void buildTree( NodePtr &root, ifstream& input )
{
char line [50];
input.getline(line, sizeof line);
if (strlen(line) == 0)
{
root = NULL;
return;
}
}
I think you are doing it wrong the format for getline() is as follows.
If you are using char array :
char buffer[256];
input.getline(buffer, 256);
If you are using string :
string buffer;
getline(input, buffer);