Program never gets to the part where I can input information - c++

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

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

C++: capitalizing a character using another character

I'm trying to make a function that capitalizes the next character in a string after I input a "^" character. the code looks like this:
void decodeshift( string orig, string search, string replace )
{
size_t pos = 0;
while (true) {
pos = orig.find(search, pos);
if(pos == string::npos)
break;
orig.erase(pos, search.length());
orig.replace(pos, search.length(), replace);
cout<<orig<<endl;
}
}
int main()
{
string question = "What is the message? ";
string answer = "The real message is ";
string shift="^";
string test="a";
string answer1;
//output decoded message
string answer2;
cout << question;
cin >> answer1;
cout << "decoding . . . " << "\n";
//decodeback(answer1, back);
decodeshift(answer1, shift, test);
return 0;
}
my input will be:
^hello
desired output:
Hello
current output
aello
I can't seem to find the correct function to use and I'm confused on how to use toupper in a situation like this. I just need to find the proper replacement.
Try something more like this:
#include <cctype>
void decodeshift( string orig, string search )
{
size_t pos = orig.find(search);
while (pos != string::npos)
{
orig.erase(pos, search.length());
if (pos == orig.size()) break;
orig[pos] = (char) std::toupper( (int)orig[pos] );
pos = orig.find(search, pos + 1);
}
return orig;
}
...
answer1 = decodeshift(answer1, "^");
cout << answer1 << endl;
Or, simply get rid of the shift parameter:
#include <cctype>
string decodeshift( string orig )
{
size_t pos = orig.find('^');
while (pos != string::npos)
{
orig.erase(pos, 1);
if (pos == orig.size()) break;
orig[pos] = (char) std::toupper( (int)orig[pos] );
pos = orig.find('^', pos + 1);
}
return orig;
}
...
answer1 = decodeshift(answer1);
cout << answer1 << endl;

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

Read and parse specific parts of the file

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;

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"