Reputation: 145
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.
Upvotes: 1
Views: 9250
Reputation: 5301
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).
Upvotes: 0
Reputation: 309
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";
}
}
Upvotes: 0
Reputation: 4835
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>{}});
}
Upvotes: 2
Reputation: 36882
First use getline to grab an entire line, then you can use a istringstream
to create a stream of int
s 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>{});
}
Upvotes: 2
Reputation: 49
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)
}
Upvotes: 1