Read and parse specific parts of the file - c++

I have an input file with the following content:
Tstart: 13:51:45
Tend: 13:58:00
and I'd like to have the timestamps in separate strings at the end. So far, I've written the following:
// open the info file
if (infile.is_open())
{
// read the info regarding the played video
string line;
while (getline(infile, line))
{
istringstream iss(line);
string token;
while (iss >> token)
{
string tStart = token.substr(0, 6);
string tEnd = token.substr(7,2);
cout << tStart << tEnd<< endl;
}
}
infile.close();
}
else
cout << "Video info file cannot be opened. Check the path." << endl;
and I get the following output:
Tstart
13:51:5
terminate called after throwing an instance of 'std::out_of_range'
what(): basic_string::substr: __pos (which is 7) > this->size() (which is 5)
I do understand what the error says, but I cannot find another way of doing this in C++.
Anyone has an idea?

String line will be one line of text. First it will be "Tstart: 13:51:45", and in the next iteration it will be "Tend: 13:58:00".
String token will be part of the line that is delimited with space. So, if line is "Tstart: 13:51:45" then token will be "Tstart:" in first iteration and "13:51:45" in second iteration. This is not what you need.
Instead of the inner while loop I suggest searching for a space with string::find and then taking everything after the space with string::substr:
bool is_first_line = true;
string tStart, tEnd;
while (getline(infile, line))
{
int space_index = line.find(' ');
if (space_index != string::npos)
{
if (is_first_line)
tStart = line.substr(space_index + 1);
else
tEnd = line.substr(space_index + 1);
}
is_first_line = false;
}
cout << tStart << tEnd << endl;
If it is not known in advance which line has which value then we can still get away from inner loop:
string tStart, tEnd;
while (getline(infile, line))
{
int space_index = line.find(' ');
if (space_index != string::npos)
{
string property_name = line.substr(0, space_index);
if (property_name == "Tstart:")
tStart = line.substr(space_index + 1);
else if (property_name == "Tend:")
tEnd = line.substr(space_index + 1);
}
}
cout << tStart << tEnd << endl;

Related

Config file parser is only returning previous value

I have a small block of code, which is used to read the configuration file and then find for the specific value based on the key. My program now looks something like this:
string readConfigFile(string configKey) {
cout << "ReadConfigFile\n";
fstream drConfig("/usr/share/dr_config");
if(drConfig.is_open()) {
string line;
string value;
while (getline(drConfig, line))
{
line.erase(std::remove_if(line.begin(), line.end(), ::isspace), line.end());
if(line[0] == '#' || line.empty()){
continue;
}
auto delimiterPos = line.find("=");
auto name = line.substr(0, delimiterPos);
value = line.substr(delimiterPos + 1);
// Use this to find a specific string
if (line.find(configKey) != std::string::npos) {
cout << value << endl;
}
}
return value;
} else {
return "Couldn't open the config file!\n";
}
}
In my main code I call it something like this:
string num1 = readConfigFile("120");
stringstream geek(num1);
int x = 0;
geek >> x;
cout << "The value of x is " << x << "\n";
string num2 = readConfigFile("100");
stringstream geek(num2);
int y= 0;
geek >> y;
cout << "The value of y is " << y<< "\n";
Supposedly, it should print me number 100 for my int y. But surprisingly, it's printing my previous value which is 120. I am thinking something is wrong in my readConfigFile() method. Can someone guide me through this? How can I get the latest value which is 100?
Actually I just found out the answer that I should add a break; once I already find the key which the code should look like this:
while (getline(drConfig, line))
{
line.erase(std::remove_if(line.begin(), line.end(), ::isspace), line.end());
if(line[0] == '#' || line.empty()){
continue;
}
auto delimiterPos = line.find("=");
auto name = line.substr(0, delimiterPos);
value = line.substr(delimiterPos + 1);
// Use this to find a specific string
if (line.find(configKey) != std::string::npos) {
cout << value << endl;
break;
}
}
return value;
Basically the break it stops the loop and then it returns me the matching value for the key.
Your code currently always returns the last value in the file. line.find(configKey) != std::string::npos also seems odd, shouldn't it just be configKey == name?
line[0] == '#' || line.empty() has undefined behaviour if you aren't using c++11 or later, its better to swap the two conditions: line.empty() || line[0] == '#'.
Try this code:
string readConfigFile(string configKey) {
cout << "ReadConfigFile\n";
fstream drConfig("/usr/share/dr_config");
if(drConfig.is_open()) {
string line;
while (getline(drConfig, line))
{
line.erase(std::remove_if(line.begin(), line.end(), ::isspace), line.end());
if(line.empty() || line[0] == '#'){
continue;
}
auto delimiterPos = line.find("=");
if (delimiterPos == std::string::npos) {
return "Invalid config file";
}
auto name = line.substr(0, delimiterPos);
auto value = line.substr(delimiterPos + 1);
// Use this to find a specific string
if (configKey == name) {
return value;
}
}
return "Couldn't find key";
} else {
return "Couldn't open the config file!\n";
}
}

Modifying a while loop that contains a break statement

I'm currently trying to write a loop to keep extracting each line from an input file using getline, and keep running until it detects the end of a paragraph.
I found this as a reference:
while (getline(inFile, line))
{
if (line.empty())
break;
else
{
for (int j = 0; j < line.length(); j++)
{
//Doing stuff here
}
}
}
I've tested this and it runs fine. However, I'm not allowed to use break statements for my assignment.
I thought I could do something like this:
while (getline(inFile, line))
{
while (!line.empty())
{
for (unsigned int i = 0; i < line.length(); i++)
{
//Do stuff
}
}
}
But the program ends up crashes when the loop iterates for the first time. I was wondering if anyone had some input how I could resolve this issue
This is how you solve it:
while (getline(inFile, line) && !line.empty()) { ... }
it has the exact same effect and it arguably improves the readability. The problem with your approach is that it causes an infinite loop. The fact that line.empty() evaluates false does not effect the outer while there.
The following also demonstrates the use of a stringstream for testing. exec contains 3 tests snippets.
int exec(int , char** )
{
int retVal = 0;
cout << "\n\n";
string s =
" now is the time\n"
" for all good men\n"
"\n" // empty line
" to come to the aid \n"; // eof()
// solution 1 has infinite loop
if(0) // disable
{
stringstream ss;
ss << s; // load ss
string line;
while (getline(ss, line))
{
while (!line.empty())
{
for (uint j=0; j < line.length(); ++j)
cout << line[j];
}
cout << endl;
}
}
// solution 2 exits too soon, line 3 is empty, line 4 dropped
{
cout << "\n\n";
stringstream ss;
ss << s; // load ss
string line;
while (getline(ss, line) && !line.empty())
{
cout << line << endl;
}
}
// output:
// now is the time
// for all good men
// solution 3 - small test effort, but seems to work
{
cout << "\n\n";
stringstream ss;
ss << s; // load ss
do
{
string line;
getline(ss, line); // read a line
if (!line.empty()) // test it has content
cout << line << endl; // use when non-empty
} while(!ss.eof()); // how continue when file has more lines
} // solution 3 seems to work
// output:
// now is the time
// for all good men
// to come to the aid
return retVal;
}

Program never gets to the part where I can input information

When I run the program the output window prints out the line "! Loading Data..." but it seems it's stuck in a loop endlessly. As far as I can tell the while loop is set up correctly, but after plugging away I am at a loss.
ifstream myfile("Data.CS.txt");
if (!myfile) { //Always test the file open.
cout << "Error opening output file" << endl;
system("pause");
return -1;
}
cout << endl;
cout << "! Loading Data...";
while (getline(myfile, line)) {
string delimiter = "|";
string delimiter2 = "-=>";
size_t pos = 0;
string tempLine;
string tokenName;
string token;
string token2;
vector <string> storeTokenPairs;
tokenName = line.substr(0, pos);
tempLine = line.erase(0, pos + delimiter.length());
while ((pos = tempLine.find(delimiter2)) != string::npos) {
token = tempLine.substr(0, pos);
storeTokenPairs.push_back(token);
line.erase(0, pos + delimiter2.length());
}
for (int i=0; i<storeTokenPairs.size(); i++)
dictionary.emplace(tokenName, make_pair(storeTokenPairs[i], storeTokenPairs[i+1]));
}
The following line of code is wrong:
while ((pos = tempLine.find(delimiter2)) != string::npos) {
token = tempLine.substr(0, pos);
storeTokenPairs.push_back(token);
line.erase(0, pos + delimiter2.length()); // <-- HERE
}
You are never modifying tempLine, so the loop runs endlessly if delimiter2 is found in tempLine.
You need to replace line with tempLine instead:
tempLine.erase(0, pos + delimiter2.length());
Alternatively, you don't actually need to modify tempLine at all, as find() takes an optional start index as input:
size_t start = 0, pos;
while ((pos = tempLine.find(delimiter2, start)) != string::npos) {
token = tempLine.substr(start, pos-start);
storeTokenPairs.push_back(token);
start = pos + delimiter2.length();
}
if (start < tempLine.length()) {
token = tempLine.substr(start);
storeTokenPairs.push_back(token);
}

Extract and set variables from input file c++

So I want to get a specific part from every line of the input file.
So far I got this:
ifstream fin("text.txt");
string line;
while (getline(fin, line)) {
if (line.find("Set") == 0)
{
istringstream sin1(line.substr(line.find("path1=") + 1));
sin1 >> path1;
istringstream sin2(line.substr(line.find("path2=") + 1));
sin2 >> path2;
}
}
From what I understood the (line.substr(line.find("VAR_CAL_PATH2=") + 1)) part will take whatever it's after "path1=" and will put it into path1, I guess I got it wrong. My input file has two lines:
Set path1="somepath"
Set path2="someotherpath"
When the while loop ends I get path1="Set" and path2="someotherpath" but what I want is path1="somepath" and path2="someotherpath"
As #Jordfräs says, find() returns the position of the start of the string.
When you try to parse the path2 value, you overwrite the value of path1.
The code with these fixes:
const string path1field = "path1=";
const string path2field = "path2=";
string path1 = "", path2 = "";
ifstream fin("text.txt");
string line;
while (getline(fin, line))
{
if (line.find("Set") != string::npos)
{
size_t path1pos = line.find(path1field);
size_t path2pos = line.find(path2field);
if (path1pos != string::npos)
{
istringstream sin1(line.substr(path1pos + path1field.length()));
sin1 >> path1;
}
if (path2pos != string::npos)
{
istringstream sin2(line.substr(path2pos + path2field.length()));
sin2 >> path2;
}
}
}
cout << "path1: " << path1 << endl;
cout << "path2: " << path2 << endl;
The find() function in std::string returns the position of the start of the string. Adding 1 will point to the start + 1, not the position after the string you search for.
There is plenty of good documentation of the standard library available, e.g., http://en.cppreference.com/w/cpp/string/basic_string/find
Another (more general) solution for string variables could be:
ifstream fin("text.txt");
string line;
const vector<string> pathsNames={"path1=", "path2="};
vector<string> paths(pathsNames.size());
while (getline(fin, line)) {
if (line.find("Set") == 0)
{
for(std::vector<string>::size_type x=0; x<pathsNames.size(); x++){
if(line.find(pathsNames[x])!=string::npos){
paths[x]=line.substr(line.find(pathsNames[x]) + pathsNames[x].length());
break;
}
}
}
}
//print results
for(std::vector<string>::size_type x=0; x<pathsNames.size(); x++){
cout << pathsNames[x] << paths[x] << endl;
}

Why doesn't this code run?

Hey, sorry if this is asked a lot but I have no idea what the problem here is.
In the C++ code below, I'm reading from a user defined input file and generating output. I've been writing it piece by piece and putting it together, compiling, testing, etc as I go to work out the bugs. This is a learning experience for me, first self-directed program I guess...
Anyways, when I run the code, the command prompt prints ONE line and goes unresponsive. I would say it has been caught in some kind of loop, but I believe that's impossible.
I think it might have something to do with the array I'm trying to declare, I wanted to make a dynamic string array but I found out that's difficult...
#include <iostream>
#include <fstream>
#include <algorithm>
#include <cctype>
#include <string>
using namespace std;
int wordCount(string line)
{
int fpos, fpos2;
int count = 0;
fpos = line.find_first_not_of(' ');
line.erase(0, fpos);
while(line.size() > 0)
{
fpos = line.find_first_of(' ');
if(line.at(0) == '"')
{
line.erase(0, 1);
for(int i = 0; i <line.size(); i++)
if(line.at(i) == '"' && line.at(i-1) != '\\')
{
fpos2 = i;
break;
}
line.erase(0, fpos2 + 2);
}
else
line.erase(0, fpos + 1);
count++;
}
return count;
}
int main()
{
//Current line; Input file; Output file;
string currentline, fileName, outFileName;
ifstream fin;
ofstream fout;
cout << "Enter input file name: ";
getline(cin, fileName);
cout << "Enter output file name: ";
getline(cin, outFileName);
fin.open(fileName.c_str());
if (!fin.good()) throw "I/O error";
fout.open(outFileName.c_str());
if (!fout.good()) throw "I/O error";
getline(fin, currentline);
while (!currentline.empty())
{
int pos, pos1;
pos = currentline.find("//");
string postScript = currentline.substr(pos+2,-1);
pos = currentline.find_first_of(';');
string xline = currentline.substr(0,pos+1);
cout << xline << endl;
int size = wordCount(xline);
string *words;
words = (string *) malloc (size*sizeof(string));
words = new string[size];
pos = xline.find_first_not_of(' ');
xline.erase(0, pos);
for ( int i = 0; i < size; i++ )
{
pos = xline.find_first_of(' ');
if ( xline.at(0) == '"' )
{
xline.erase(0, 1);
for(int a = 0; a < xline.size(); a++) //This for loop finds the end of a quoted statement within the line.
if ( xline.at(a) == '"' && xline.at(a-1) != '\\' )
{
pos = a;
break;
}
words[i] = xline.substr(0,pos);
xline.erase(0,pos + 2);
}
else
{
words[i] = xline.substr(0,pos);
xline.erase(0,pos + 1);
}
cout << words[i] << endl;
}
cout << xline << endl << endl;
getline(fin, currentline);
}
return 0;
}
I would suggest you commenting out bits of code until it starts to work the way you expect (Usually the problematic bit will become obvious this way.) Once you figure out what is wrong you can ask a more specific question on StackOverflow.
You should use a debugger to investigate the program behavior.
To avoid single stepping the whole program, you can set breakpoints where you expect to passs the sequence. When a breakpoint is not hit you can use single stepping from the previous point. Additionally you can look at variables content.
It never finds the end quote:
if ( xline.at(a) == '"' && xline.at(a-1) != '\\' )
{
pos = a;
break;
}
Try this instead:
if (xline.at(a) == '"')
{
pos = a;
break;
}
You only need to escape " if its contained in a string literal, e.g. "There's a \" in this literal"