I am trying to parse a string that looks like "1,4-6,8-10,12" and push_back the results into a vector of ints/char*. While parsing, if the logic comes across 4-6 then it should push the ints 4,5 and 6 in the vector. I am trying to do this using strtok but it modifies the only copy of the input string so I am not getting anywhere. I cannot use boost or else tokenizer would have been very easy and useful.
#include <stlport\sstream>
#include <stlport\vector>
using namespace std;
...
stringstream ss("1,4-6,8-10,12");
vector<int> v;
int x, x2;
char c;
while (ss >> x)
{
v.push_back(x);
if (!(ss >> c))
break; // end of input string
if (c == '-')
{
if (!(ss >> x2))
throw; // incorrect input string
for (int i = x+1; i <= x2; i++)
v.push_back(i);
if (!(ss >> c))
break; // end of input string
}
else if (c != ',')
throw; // incorrect input string
}
// check
int s = v.size();
// s = 8, v:{1,4,5,6,8,9,10,12}
std::stringstream ss("1,4-6,8-10,12");
std::vector<int> v;
int x;
while(ss >> x)
{
v.push_back(x);
char c;
ss >> c; //will just discard a non space char.
if(c != ',' || c != '-') ss.unget(); //... unless is just , or -
}
Time to write this: 1 minute.
Time to search for an appropriate algorithm function: 5 minutes at least.
Decide yourself what's more productive.
Related
I was reading the following code sample:
std::array<int, 4> parseMessage(const std::string& input) {
std::stringstream ss(input);
std::array<int, 4> message;
int n;
// Loop over all characters in the string and ignore the semicolons.
for (int i = 0; ss >> n && i < 4; ++i) {
message[i] = n;
if (ss.peek() == ';') {
ss.ignore();
}
}
return message;
}
Can someone explain why someone would do ss>>n in the loop condition area ? It looks a bit odd to me.
It will first read a an int from the stream and then evaluate if the stream is good. (because s >> n will return a reference to s).
Evaluating a stream as bool:
Returns true if the stream has no errors and is ready for I/O operations. Specifically, returns !fail().
So as soon as the stream fails to read an int or reaches the end of stream (e.g. end of input) it will evaluate as false and end the loop.
So this code will extract up to 4 ints from a given input (4, because of the && i < 4).
>> reads into the variable on the right hand side of the expression and returns the stream.
Using a stream in a boolean expression returns whether the stream has not failed.
Your for loop is therefore equivalent to:
int i = 0;
while ( i < 4 )
{
ss >> n;
if ( ss.fail() )
{
break;
}
message[i] = n;
if (ss.peek() == ';') {
ss.ignore();
}
i++;
}
It reads up to 4 numbers and stops if the contents of the stream are not convertible to a number.
As I'm familiarizing myself with the I/O aspect of C++, I'm trying to write a program to read some lines of integers from std::cin. Say the input looks like this:
1 2 3
4 5 6
7 8 9
10 11 12
How can I read the above lines into a 2D vector?
vector<vector<int>> nums;
/*
... some code here and nums will look like the following:
nums = {
{1,2,3},
{4,5,6},
{7,8,9},
{10,11,12}
}
*/
I've also tried to read the above lines of integers to a 1D vector, but I'm having some issues dealing with the '\n' character. My code is:
string rawInput;
vector<int> temp;
while(getline(cin, rawInput, ' ') ){
int num = atoi( rawInput.c_str() );
temp.push_back(num);
}
And the final result I got by printing out all the elements in the "temp" vector is:
1 2 3 5 6 8 9 11 12 // 4, 7, 10 went missing
Any help is appreciated. Thank you.
First use getline to grab an entire line, then you can use a istringstream to create a stream of ints just for that line.
At that point it's just a matter of creating each subvector of ints using the vector constructor that takes two iterators. An istream_iterator<int> on your istringstream gets this done:
std::vector<std::vector<int>> nums;
std::string line;
while (std::getline(std::cin, line)) {
std::istringstream ss(line);
nums.emplace_back(std::istream_iterator<int>{ss}, std::istream_iterator<int>{});
}
What is happening is since you are using only ' '(space) as deliminator, the input happens to be
1
2
3\n4 //<------ Newline also comes with the input
...
So, you are passing 3\n4, 6\n7 etc to atoi it returns 3,6 etc(atoi parses the input till first non-digit input) and the 4,7 is lost.
To achieve want you want you can use getline with istringstream (keeping the default deliminator as newline)
string rawInput;
vector<vector<int>> temp;
while(getline(cin, rawInput) ){
istringstream bufferInput(rawInput);
temp.push_back(vector<int>{std::istream_iterator<int>{bufferInput}, std::istream_iterator<int>{}});
}
you can use stringstream
string rawInput;
vector<int> temp;
stringstream ss;
while(getline(cin,rawInput)){
ss<<rawInput;
vector<int> temp;
int x;
while(ss>>x){
temp.push_back(x);
}
num.push_back(temp)
}
I recently wrote an answer to another question but with a few adaptations it achieves exactly what you are looking for (I hope):
#ifndef _IOSTREAM_H
#include <iostream>
#endif
#ifndef _STRING_H
#include <string>
#endif
#ifndef _VECTOR_H
#include <vector>
#endif
using namespace std;
enum XYZ { X = 0, Y = 1, Z = 2 };
struct Vector {
float x, y, z;
Vector(float _x=0, float _y=0, float _z=0) {
x = _x;
y = _y;
z = _z;
}
float& operator[](size_t index) {
if (index == XYZ::X) return x;
if (index == XYZ::Y) return y;
if (index == XYZ::Z) return z;
throw new exception;
}
};
#define min(a, b) (((a) < (b)) ? (a) : (b))
bool isCharNumeric(char c) {
const char* numbers = "0123456789";
for (size_t index = 0; index < strlen(numbers); index++)
if (c == numbers[index]) return true; return false;
}
vector<Vector> parseNumbers(string str_in) {
str_in += " "; //safe, no out of bounds
vector<Vector> results = {};
char currentChar;
char skipChar = ' ';
bool found_period = false;
size_t count_len = 0;
Vector vector_buffer(0,0,0);
XYZ current_axis = (XYZ)0;
for (size_t index = 0; index < str_in.length(); index++) {
currentChar = str_in[index];
if (currentChar == skipChar || currentChar == '\n' || currentChar == '\t')
continue;
else if (isCharNumeric(currentChar)) {
string word = ""; //word buffer
size_t word_len = min(min(str_in.find_first_of(' ', index + 1) - (index), str_in.find_first_of('\n', index + 1) - (index)), str_in.find_first_of('\t', index + 1) - (index)); //whatever char comes first; newline, tab or space
//append chars of following word checking if it is still valid number char
if (word_len > 0) {
size_t count_word_len = 0;
for (count_word_len = 0; count_word_len < word_len; count_word_len++)
if (isCharNumeric(str_in[index + count_word_len])) {
word += str_in[index + count_word_len];
}
else if (str_in[index + count_word_len] == '.' && isCharNumeric(str_in[index + count_word_len + 1])) {
//Floating-point numbers
word += '.';
found_period = true;
continue;
}
else {
word = "";
continue;
}
vector_buffer[current_axis] = stof(word);
if (current_axis == XYZ::Z) {
current_axis = XYZ::X;
results.push_back(vector_buffer);
}
else {
current_axis = (XYZ)(current_axis + 1);
}
index += count_word_len;
word = "";
continue;
}
}
}
return results;
}
Example implementation:
int main(int argc, char** argv) {
string user_input;
cin >> user_input;
vector<Vector> numbers = parseNumbers(user_input);
for each (Vector v in numbers) {
cout << "X=" << v.X << "\n";
cout << "Y=" << v.Y << "\n";
cout << "Z=" << v.Z << "\n\n";
}
}
Suprisingly none of the answers use the istream stream operator:
http://www.cplusplus.com/reference/istream/istream/operator%3E%3E/
When stream is empty eofbit is set, so run a while loop on that.
Works great for all types, and can be overloaded for custom types (such as 2D texture).
I'm a usual lurker but this is my first post! I understand you guys like detail so I will do my best. I will appreciate whatever input anyone has.
I am working on an overloading the extraction operator for an object with a dynamic array of digits. The console input will have leading white space, then an int, then anything after. I need to ignore white space, extract the int, and then leave the rest alone. Easy right?
Here is an example of code I found online:
istream & operator >> (istream &m, MyInt & p)
{
int x = 0;
p.currentLength = 0;
while ((m.peek() == '\n') || (m.peek() == '\0') ||
(m.peek() == '\t') || (m.peek() == ' '))
{
m.get();
}
while ((m.peek() >= '0') && (m.peek() <= '9'))
{
if (p.currentLength >= p.maxSize)
{
p.grow();
}
m >> p.theNumber[x];
x++;
p.currentLength++;
}
m.get();
// reverse the order (i.e. - 123 to 321)
char * temp = new char[p.maxSize];
for (int y = 0; y < p.currentLength; y++)
{
temp[y] = p.theNumber[p.currentLength - 1 - y];
}
delete [] p.theNumber;
p.theNumber = temp;
return m;
}
Now, I understand this method may work, however to me, that seems like an extremmeelly inefficient method. For a trillion digit number, Grow() would reallocate the array a trillion times! Perhaps this is not as bad as I think it is?
My current method has been using seekg() and peek() and get(). Like so:
istream& operator >> (istream& is, MyInt& z)
{
int i = 0, j = 0;
// check if next char is white
while (is.peek() == 38)
{
j++;
is.seekg(j); // skip if white
}
while (isdigit(is.peek()))
{
i++;
is.seekg(j + i);
if (!is.peek())
{
is.clear();
break;
}
}
is.seekg(j);
z.length = i;
z.digits = new int[i + 1];
for (i = 0; i < z.length; i++)
{
z.digits[i] = C2I(is.get());
}
return is;
}
Also, here is my main:
int main()
{
MyInt B;
cout << "\n\nChange B to what num? ---> ";
cin >> B;
cout << "B is now: " << B;
char c;
cout << "\n\n\n\n\nEnter char to exit : ";
cin >> c;
return 0;
}
For the life of me I can not find what is causing my program to exit. The last output seems to say, 'B is now: -1'
I believe the this means the << B failed. I have B initialized to 0 currently, and the rest of my code has presented no other issues. It's private member data only include the pointer and a length (num of digits). Also C2I() is a function that converts '0' through '9' to 0 through 9.
A big issue for me is I am fairly new to parsing, so I don't have very eloquent ways to test this, or other ideas.
Again I appreciate everything you guys do. I have already learned a great deal from browsing here!
So I have a data file that looks something like this:
x + y + z
30 45 50
10 20 30
The only characters I needed was the operators, so '+' '+' I was able to use file.get() to successfully get those characters and put them in an array. Problem is I need to get the next line of numbers, and assign them to the values x , y z . I know I cant use .get() , I would have to use getline. Will i have to eliminate the file.get() and use getline instead for first part also?
I looked at some of the questions posted on here but none of them were quite like mines. Note I'm actually going to be using these values for another part of my program, just used cout to see if my values were being read correctly
Here's my previous code:
main(int argc, char *argv[])
{
int a=0;
int n;
fstream datafile;
char ch;
pid_t pid;
int a, b, c, result;
string line;
datafile.open("data1.txt");
if(datafile)
{
for(int i=0; i <9; i++)
{
datafile.get(ch);
if (ch == '*'||ch == '/'||ch == '+'||ch == '-')
{
operations[a] = ch;
cout<<operations[a];
a++;
}
}
}
else
cout<<"Error reading file";
}
So this is how I was getting the first line of the file in the beginning. It worked like I wanted it to, may have not been the nicest coding but it worked. Nevertheless I tried to get the rest of the file, this time using getline, but instead of getting the numbers I was getting a bunch of random gibberish/numbers. I know if I use getline, the first line cannot be in my loop. I know this is how I would get the numbers.
while(getline(datafile, line))
{
istringstream ss(line);
ss >> x >> y >> z;
cout<<x<<""<<y<<""<<z;
}
Would the following make sense for the first line, or am I missing something:
string input;
std::getline(datafile, input)
for (int i = 0; i < input.size(); i++)
if (input[i] == '+' || ...)
{
operations[a] = input[i];
a++;
}
If you don't want to use getline, you could simply read the entire file stream (note that the bool is a rather naive way to handle the problem, I'd recommend something more elegant in your actual code):
bool first = true;
string nums;
int lines = 0;
vector<vector<int>> numlines;
vector<int> topush;
while (!datafile.eof())
{
char ch = datafile.get()
if (ch == 12 && first) //I don't know if '\n' is valid, I'd assume it is but here's the sure bet
first = false;
else if (first && (ch == '+' || ...))
{
operator[a] = ch;
a++;
}
else if (!first && (ch >= '0' && ch <= '9'))
{
if (!(datafile.peek() >= '0' && datafile.peek() <= '0'))
{
numlines[lines].push_back(atoi(nums.c_str());
nums.clear();
if (datafile.peek() == 12)
{
numlines.push_back(topush);
lines++;
}
}
else
nums = nums + ch;
}
Honestly, I can't be sure the above will work exactly right, I'd recommend you just modify your code to use getline exclusively. You'll need to add #include to get atoi.
Add this to your code:
while(!datafile.eof()){
string s;
getline(datafile, s);
istringstream in(s);
string tmp;
while(in >> tmp){
int i = stoi(tmp)
//Do something with i....
}
}
Assume that I have an input as follows:
N (X_1,Y_1) (X_2,Y_2) .... (X_N, Y_N)
where N, X_i and Y_i are integers.
An example:
2 (55,1) (521,7)
To read this, I can do something like this(assume all variables are defined, etc.):
fscanf(fin,"%d ",&N);
for (int i = 0; i < N; i++)
fscanf(fin,"(%d,%d) ", &X[i], &Y[i]);
The question is, how can I do this easily using ifstream. I can get string's, and then I can get rid of nondigits and using stringstream I can get two numbers but this seems a bit cumbersome. Is there an easier, more elegant way?
Thanks
int n, x, y;
char c;
if (is >> n)
for (int i = 0; i < n; ++i)
if (is >> c && c == '(' &&
is >> x &&
is >> c && c == ',' &&
is >> y &&
is >> c && c == ')')
{
X[i] = x;
Y[i] = y;
}
else
throw std::runtime_error("invalid inputs");
You can simplify the all-important inner if condition above to...
is >> chlit('(') >> x >> chlit(',') >> y >> chlit(')')
...with a simple support type for consuming a specific character:
struct chlit
{
chlit(char c) : c_(c) { }
char c_;
};
inline std::istream& operator>>(std::istream& is, chlit x)
{
char c;
if (is >> c && c != x.c_)
is.setstate(std::iostream::failbit);
return is;
}
See a complete program illustrating this on ideone here.
An old post of mine did something similar for consuming specific strings. (The above chlit could be a template, but chlit<','>() is ugly to read and write - I'd rather trust the compiler).
cin >> N;
for (int i = 0; i < N; i++)
{
cin.ignore(100,'(');
cin >> X[i];
cin.ignore(100,',');
cin >> Y[i];
cin.ignore(100,')');
}
It can handle whitespaces also, as it can read input like:
2 ( 1 , 3 ) ( 5 , 6 )
Demonstration at ideone: http://www.ideone.com/hO0xG