Reputation: 41
Here is my code. It is supposed to get input until end of input and place that input into the string data. Then it is supposed to tokenize the input using the delimiter "#". I then repeatedly call my function nexttoken() to store the tokens in variables.
std::istream_iterator<char> it(std::cin);
std::istream_iterator<char> end;
std::string data(it,end);
std::string delimiter = "#";
StringTokenizer strtok(data,delimiter);
std::string t1 = strtok.nextToken();
std::string t2 = strtok.nextToken();
This all works when I pass a file through on command line like this:
program.exe <testcase1.txt
testcase1.txt
S A B #
S -> a A #
S -> z B #
A -> b B c B #
B -> d A #
##
output
S A B
a: 1
b: 1
c: 1
d: 1
z: 1
everything checks out and works.
My problem is this: When I push run in my IDE I can type the input in manually but when I do there is no way to make the program accept the input except if I press ctrl-z. This problem also persists in linux when I pass a file through in terminal, it just hangs there letting me type infinite lines.
here is a smaller version of my code that only counts 3 tokens and only checks for a,b and c
main.cpp
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
#include <iterator>
#include <algorithm>
#include <cstddef>
#include "StringTokenizer.h"
int countSubstring(const std::string& str, const std::string& sub)
{
if (sub.length() == 0) return 0;
int count = 0;
for (size_t offset = str.find(sub); offset != std::string::npos;
offset = str.find(sub, offset + sub.length()))
{
++count;
}
return count;
}
int main(int argc, char* argv[1])
{
int task;
if (argc < 2)
{
std::cout << "Error: missing argument\n";
return 1;
}
task = atoi(argv[1]);
switch(task){
case 0:
{
std::istream_iterator<char> it(std::cin);
std::istream_iterator<char> end;
std::string data(it,end);
std::string delimiter = "#";
StringTokenizer strtok(data,delimiter);
int a = 0;
int b = 0;
int c = 0;
//reading the first token and puting it in tk1
std::string t1 = strtok.nextToken();
std::string tk1(t1);
tk1.erase(0, tk1.find_first_not_of(" "));
tk1.erase(tk1.find_last_not_of(" ")+1);
// token 2 and 3 are different because 1 is always the same format
std::string t2 = strtok.nextToken();
std::string tk2(t2);
if(countSubstring(tk2,"a") > 0)
{
a = a + 1;
}
if(countSubstring(tk2,"b") > 0)
{
b=b + 1;
}
if(countSubstring(tk2,"c") > 0)
{
c=c + 1;
}
std::string t3 = strtok.nextToken();
std::string tk3(t3);
if(countSubstring(tk3,"a") > 0)
{
a = a + 1;
}
if(countSubstring(tk3,"b") > 0)
{
b=b + 1;
}
if(countSubstring(tk3,"c") > 0)
{
c=c + 1;
}
// this is where the output is
std::cout << tk1 << std::endl;
if(a > 0)
{
std::cout << "a: " << a <<std::endl;
}
if(b > 0)
{
std::cout << "b: " << b <<std::endl;
}
if(c > 0)
{
std::cout << "c: " << c <<std::endl;
}
}
break;
//////////////////////////////////////////////////
case 1:
break;
case 2:
break;
default:
std::cout << "Error: unrecognized task number " << task << "\n";
break;
}
return 0;
}
StringTokenizer.h
#ifndef INCLUDE_STRINGTOKENIZER_H
#define INCLUDE_STRINGTOKENIZER_H
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string>
class StringTokenizer
{
public:
StringTokenizer(const std::string& _str, const std::string& _delim);
~StringTokenizer(){};
std::string nextToken();
std::string nextToken(const std::string& delim);
private:
std::string token_str;
std::string delim;
};
#endif
StringTokenizer.cpp
#include "StringTokenizer.h"
StringTokenizer::StringTokenizer(const std::string& _str, const std::string& _delim)
{
if ((_str.length() == 0) || (_delim.length() == 0)) return;
token_str = _str;
delim = _delim;
/*
Remove sequential delimiter
*/
unsigned int curr_pos = 0;
while(true)
{
if ((curr_pos = token_str.find(delim,curr_pos)) != std::string::npos)
{
curr_pos += delim.length();
while(token_str.find(delim,curr_pos) == curr_pos)
{
token_str.erase(curr_pos,delim.length());
}
}
else
break;
}
/*
Trim leading delimiter
*/
if (token_str.find(delim,0) == 0)
{
token_str.erase(0,delim.length());
}
/*
Trim ending delimiter
*/
curr_pos = 0;
if ((curr_pos = token_str.rfind(delim)) != std::string::npos)
{
if (curr_pos != (token_str.length() - delim.length())) return;
token_str.erase(token_str.length() - delim.length(),delim.length());
}
}
std::string StringTokenizer::nextToken()
{
if (token_str.length() == 0)
return "";
std::string tmp_str = "";
unsigned int pos = token_str.find(delim,0);
if (pos != std::string::npos)
{
tmp_str = token_str.substr(0,pos);
token_str = token_str.substr(pos+delim.length(),token_str.length()-pos);
}
else
{
tmp_str = token_str.substr(0,token_str.length());
token_str = "";
}
return tmp_str;
}
std::string StringTokenizer::nextToken(const std::string& delimiter)
{
if (token_str.length() == 0)
return "";
std::string tmp_str = "";
unsigned int pos = token_str.find(delimiter,0);
if (pos != std::string::npos)
{
tmp_str = token_str.substr(0,pos);
token_str = token_str.substr(pos + delimiter.length(),token_str.length() - pos);
}
else
{
tmp_str = token_str.substr(0,token_str.length());
token_str = "";
}
return tmp_str;
}
1: How do I change my code so that It will stop searching for input when I am done typing? or when it can see ## has been typed? (## marks the end of the input)
2: Is this even possible with my current code?
Both Linux and my IDE compile with g++
Upvotes: 2
Views: 1898
Reputation: 208456
You are using input streams from std::cid
to read the data, which will only stop when you reach the end-of-file, which is why you need to terminate the input with Ctrl-z in windows and Ctrl-d in Linux.
The simplest change is reading line by line and processing them independently. That will allow you to read the termination marker ##
and not proceed further (assuming that the marker is actually two #
followed by a new line).
std::string line;
while (std::getline(std::cin, line)) {
if (line == "##") break;
// process a single line
}
If there's no guarantee that the delimiter is followed by a single line, you may need to read character by character, but that is unlikely.
Upvotes: 1