Reading variable size 2D-STL vector from file - c++

2
1 3
2 4 8 13
3 5 6 13
4
4
8
3 7 9 10 13
8 10 11
8 9 11 12
9 10
10 15
3 4 8 14
13
12 16
15 17 18
16
18 16
I want to read this these values from file into two dimensional STL vector. Note that the size of inner vector is not uniform and not known in adance so I need to detect '\n'. So far I have been unnsuccessful. My code is as follows. Please help. What is wrong?
int main()
{
ifstream Lin("A.txt");
double d;
vector<vector<double> > A;
vector<double> dummy;
if (Lin.is_open()){
while (Lin >> d) {
if (Lin.peek() == '\n'){
A.push_back(dummy);
dummy.clear();
}else{
dummy.push_back(d);
}
}
Lin.close();
}
...
return 0;
}
When I iterate over the vector using the following code
, it reveals what is stored:
for(int i = 0; i< A.size(); i++){
for(int j = 0; j< A[i].size() ; j++){
cout << A[i][j] << " ";
}
cout << endl;
}
1
2 4 8
3 5 6
3 7 9 10
8 10
8 9 11
9
10
3 4 8
12
15 17
18
The intended output is same as the way it is stored in the file

Make sure that there is no empty space after the last integer in each line in the data file.
In the code currently you are not adding the last integer to the dummy vector. Modify it like this:
while (Lin >> d)
{
dummy.push_back(d); // Add the number first
if (Lin.peek() == '\n') // Then check if it is end of line
{
A.push_back(dummy);
dummy.clear();
}
}

Less is more. This replaces your entire loop. Note: You don't need to check is_open or call close for this to work safely.
for(std::string s; std::getline(Lin, s);)
{
A.emplace_back(std::istream_iterator<double>(std::istringstream(s)),
std::istream_iterator<double>());
}

Consider using getline.
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
int main() {
std::string line;
std::vector<std::vector<double> > v;
while(std::getline(cin, line)) {
std::stringstream ss(line);
double value;
std::vector<double> numbers;
while(ss >> value) {
numbers.push_back(value);
std::cout << value << std::endl;
}
v.push_back(numbers);
}
return 0;
}

There could be other whitespace characters after the number at the end of line, other than \n. Like spaces:
1234 445 445 \n
^^^^
So your approach is not safe. Better would be to read entire line (getline) to some string, then construct istringstream on this string containing line, then just parse this istringstream to your vector for one line.
Like this:
for (string line; getline(filein, line); ) {
vector<double> dd;
istringstream is(line);
for (double d; is >> d; )
dd.push_back(d);
if (not dd.empty())
A.push_back(dd);
}

Related

Sorting a text file which contains integers, sorting has to be done line by line

I want to sort a file which contains integers but wanted to sort only line by line. I tried, but I am sure what I am doing below isn't in-place.
void sort_file_in_place(const char* filename) {
fstream sort_file;
sort_file.open(filename, ios::out | ios::in);
vector <vector <int> > input_vect;
string line;
std::string delimiter = " ";
int vect_index = 0;
while (getline(cin, line)) {
//store into vector of vectors
//tokenise string
size_t pos = 0;
int value = 0;
int count = 0;
while ((pos = line.find(delimiter)) != std::string::npos) {
string token = line.substr(0, pos);
line.erase(0, pos + delimiter.length());
value = std::atoi(token.c_str());
count++;
}
input_vect.resize(vect_index++, vector<int>(count));
}
//sort vector of vectors
for (int i = 0; i < input_vect.size(); i++) {
sort(input_vect[i].begin(), input_vect[i].end(), std::less<int>());
//print the sorted vectors and alos verify the content
for (auto v : input_vect[i]) cout << v << " ";
}
//write back into the file
//Todo
}
The above code isn't complete but I hope you get what I wanted to ask.
Please suggest a better solution.
Also I want to sort these lines using different threads, for example:
If the file content is:
13 38 13 100 4 234 85 34 0 100 88
1 4 5 3 8 9 1 10
111 10 112 3 4 098 194
5 4 8 3 9 13 24 78 09 99
I want to create four threads to sort each line in-place.
I cannot see how to do it "in-place". Perhaps with some black magic we could sort elements without having them somewhere in memory but I don't have knowledge about such an occult science :)
Joking aside, if storing the lines into a std::vector does not suit your needs, you can use any other container you may want, even a raw array or a custom one. But in any case, you have to store the lines somewhere to handle them (to sort them in this case).
I have written an example to achieve what you want:
#include <vector>
#include <fstream>
#include <algorithm>
#include <thread>
std::vector<std::string> split(const std::string & s, char c)
{
std::vector<std::string> splitted;
std::string word;
for(char ch : s)
{
if((ch == c) && (!word.empty()))
{
splitted.push_back(word);
word.clear();
}
else
word += ch;
}
if(!word.empty())
splitted.push_back(word);
return splitted;
}
bool read_data(const std::string & file_path, std::vector<std::vector<int>> & data)
{
data.clear();
std::ifstream in_s(file_path);
if(!in_s)
return false;
std::string line;
std::vector <int> row;
while(std::getline(in_s, line))
{
if(!line.empty())
{
for(auto s : split(line, ' '))
row.push_back(std::stoi(s));
data.push_back(row);
row.clear();
}
}
in_s.close();
return true;
}
bool write_data(const std::string & file_path, const std::vector<std::vector<int>> & data)
{
std::ofstream out_s(file_path);
if(!out_s)
return false;
for(const auto & row : data)
{
for(size_t i = 0; i < row.size(); ++i)
{
if(i == 0)
out_s << row[i];
else
{
out_s << ((row[i] < 0) ? (" ") : (" ")) << row[i];
}
}
out_s << std::endl;
}
out_s.close();
return true;
}
void sort_line(std::vector<int> & line)
{
std::sort(line.begin(), line.end(), [](const int & i, const int & j){return i<j;});
}
void sort_data_lines(std::vector<std::vector<int>> & data)
{
std::vector<std::thread> thread_mem;
for(auto & row : data)
{
thread_mem.push_back(std::thread(sort_line, std::ref(row)));
}
for(auto & t : thread_mem)
t.join();
}
I have separated the code in three main functions, read_data() to read data from a file, sort_data_lines() to sort the data and write_data() to write data into a file.
It may be used as follows:
int main()
{
const std::string in_file_path("path/to/the/input_file.txt");
const std::string out_file_path("path/to/the/output_file.txt");
std::vector<std::vector<int>> data;
if(!read_data(in_file_path, data)) // Read the data
return -1;
sort_data_lines(data); // Sort the data
if(!write_data(out_file_path, data)) // Write the sorted data
return -2;
return 0;
}
I have multi-threaded the sorting of each line (as asked) but I think it may be important to notice that it is not a good idea if your file contains a lot of lines (and consequently, a lot of simultaneously running threads).
Usually, fix a limit at between 4 and 8 running threads is a good choice.
Of course, if your file is supposed to contain only four lines, there is no problem since you'll only have 4 running threads :)
I've made the test with the following input file:
5 2 4 3 1 -3 8 -4 -1 0 1 0 6
6 9 3 1 5 2 6 8 -7 -1 0 0 -3
2 7 9 3 4 5 -5 6 -1 0 2 -2 3
0 -6 -2 8 5 -1 4 5 -6 7 2 2 0
And I obtained the following output file:
-4 -3 -1 0 0 1 1 2 3 4 5 6 8
-7 -3 -1 0 0 1 2 3 5 6 6 8 9
-5 -2 -1 0 2 2 3 3 4 5 6 7 9
-6 -6 -2 -1 0 0 2 2 4 5 5 7 8
Hope it can help a bit.

How to read input to a C++ program in reverse order?

Suppose I give input to a C++ program as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
C++ code:
int n;
for (int i = 0; i < 15; i++)
{
std::cin >> n;
// use the value of n to make changes
}
In the above code I can read the input sequentially,
i.e. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15.
Is there any way to read input directly (without using extra memory in C++ program) from the input stream in the following order:
5 4 3 2 1 10 9 8 7 6 15 14 13 12 11
First five digits in reverse order 5 4 3 2 1, next five digits in reverse order 10 9 8 7 6, ...
No.
You need to read the data as they flow into the input stream.
In your program then, you can re-order them in any way you like.
As noted in the comments, the best way to do this is to read them in the order they are given and sort them afterwards.
// vector to hold the values
std::vector<int> values;
values.reserve(15); // reserve for better performance
int n;
for (int i = 0; i < 15; i++)
{
std::cin >> n;
values.push_back(n); // add value to back of vector
}
// sort the vector
std::sort(values.begin(), values.end());
// use the values in ascending order...
for (int i = 0; i < 15; i++) {
std::cout << values[i];
}
Yes this is possible ,but it increase the running time complexity of the code .
First you can make the outer loop for how many series it insert , in the above test case its 3.
Secondly you can make a inner loop which adds the number .
I am not sure the code is running but the logic helps you .
I am implementing the psuedo code using stack !
int n=5;
for(int count = 1 ; count <= 3 ;count++)
{
for(int i=n ; i > n-5 ; i++)
{
push_on_stack(i);
}
n=n+5;
}
You need to wrap your taking input in some functionality that reorders the values for you. You might be able to do so outside the program, i.e., pipe the input stream through another program that reorders the values. Then your code might already work as you have it now.
Or you do this wrapping in your program. For example using a custom stream-like class that buffers the values in between.
There's no way to get this done without using extra memory to buffer the values you don't yet need.
Example:
#include <iostream>
#include <stack>
struct reader {
std::stack<int> data;
reader& operator>>(int & i) {
if (data.empty()) {
while (data.size() < 5) {
data.push(0);
std::cin >> data.top();
}
}
i = data.top();
data.pop();
return *this;
}
explicit operator bool() const { return bool(std::cin); }
};
int main () {
reader r;
int i;
while (r >> i) {
std::cout << i << std::endl;
}
}
Example output:
$ g++ tt.cc -std=c++11 && echo "1 2 3 4 5 6 7 8 9 10" | ./a.out
5
4
3
2
1
10
9
8
7
6
A more straight forward approach would probably be something like this:
#include <iostream>
#include <vector>
int main () {
std::vector<int> buffer;
for (int i; std::cin >> i; ) {
buffer.push_back(i);
if (buffer.size() == 5) {
// do something with buffer
//std::vector<int> reversed(buffer.rbegin(), buffer.rend());
while (!buffer.empty()) {
std::cout << buffer.back() << "\n";
buffer.pop_back();
}
}
}
}

Inserting a Row into a 2D Vector(vector of vectors)

Relatively new to the C++ scene. Anyways, I have a list:
1 5 6
3 1 2
5 3 4 7
6 2
7 3
I am attempting to fill out the numbers that are missing (in this case, 2 and 4). This list is being inputted as a vector of vectors. That's working fine. My function to insert numbers where they need to be is not. This is my code:
#include<iostream>
#include<vector>
#include<string>
#include<fstream>
#include<sstream>
using namespace std;
int main()
{
vector< vector<int> > vec1;
vector<int> tempVec;
string str = "";
stringstream ss;
int temp = 0;
ifstream iFile;
// iFile.open("E:\\COMP 220\\SCC sample input.txt");
iFile.open("E:\\SCC sample input2.txt");
if (iFile.is_open()) //Inputs file
{
cout << "File is open!\n";
getline(iFile, str);
while (!iFile.eof())
{
int z = 0;
stringstream ss(str);
while (ss >> temp)
{
tempVec.push_back(temp);
cout << tempVec[z] << " ";
z++;
}
vec1.push_back(tempVec);
cout << endl;
tempVec.clear();
getline(iFile, str);
}
}
int count = 0;
while (count < vec1.size() - 1)
{
if (vec1[count][0] != vec1[count + 1][0] - 1)
{
tempVec.clear();
tempVec.push_back(vec1[count][0] + 1);
vec1.insert(count, tempVec);
}
else
count++;
}
return 0;
}
When finished, I was hoping the code would resemble:
1 5 6
2
3 1 2
4
5 3 4 7
6 2
7 3
Any ideas? The current problem is a compiler error with:
vec1.insert(count, tempVec);
You get the compiler error since count is an integer, and std::vector::insert's first parameter is an iterator, not an integer.
To get to a certain position in a vector using an iterator, use the vector::begin() iterator and add on the position:
So this:
vec1.insert(count, tempVec);
should be
vec1.insert(vec1.begin() + count, tempVec);

Read integer data from a file

I am just getting started on C++ and am working on codeval questions, so if anyones done that, they'll recognize this problem as it's the first on the list. I need to open a file that has 3 columns of space separated integer values. Here is mine, under fizbuz.txt. I need to get the integer values from the file and store them for later use elsewhere in the program.
1 2 10
3 5 15
4 5 20
2 8 12
2 4 10
3 6 18
2 3 11
8 9 10
2 5 8
4 9 25
Now I can open the file just fine, and I've used getline() to read the files just fine using my below code. However, I don't want them in string format, I'd like them as integers. So I looked around and everyone basically says the same notation (file>>int1>>int2...). I've written some code exactly how I've seen it in a few examples, and it does not behave at all like they're telling me it should.
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
string filename = "fizbuz.txt";
string line;
int d1,d2,len;
int i =0;
int res1[10], res2[10], length[10];
ifstream read (filename.c_str());
if (read.is_open())
{
// while(read>>d1>>d2>>len);
// {
// res1[i] = d1;
// res2[i] = d2;
// length[i] = len;
// i++;
// }
while (!read.eof())
{
read>>d1>>d2>>len;
res1[i] = d1;
res2[i] = d2;
length[i] = len;
}
read.close();
}
else
{
cout << "unable to open file\n";
}
for (int j = 0; j < 10;j++)
{
cout<< res1[j] << " " << res2[j] << " " << length[j] << '\n';
}
}
Both of the while loops perform the same in the output function at the bottom. The last line of fizbuz.txt will be returned to the first elements of res1,res2 and length, and the remaining elements of all 3 are psuedorandom values, presumably from whatever program was using that memory block before. ex output below
4 9 25
32767 32531 32767
-1407116911 4195256 -1405052128
32531 0 32531
0 0 1
0 1 0
-1405052128 807 -1404914400
32531 1 32531
-1405054976 1 -1404915256
32531 0 32531
The first version should work except that you need to remove the ; in the while line.
while (read >> d1 >> d2 >> len);
^
Try this
while (!read.eof())
{
read>>d1>>d2>>len;
res1[i] = d1;
res2[i] = d2;
length[i] = len;
i++;
}

Reading input from file and populating array to store data for later use

I'm trying to make multiple arrays of maximum size 10 that hold the contents of an input file. Suppose the file is formatted like:
1 3 2 4 0 5 8 -3
3 1 4 6 0 3
4 83 2 12 1 3
4 30 4 -2
Where each line cannot exceed more than 10 integers.
void read_input(ifstream &file)
{
vector<array<int, 10> > main_array;
while(!(file.eof()))
{
int idx = 0;
string values;
array<int, 10> part_array;
getline(file, values);
istringstream inStream;
inStream.str(values);
while((inStream >> part_array[idx++]).good() == true);
main_array.push_back(part_array);
file.ignore();
}
for(auto it = main_array.begin(); it != main_array.end(); it++)
{
for(auto& s: *it)
{
cout << s << " ";
}
cout << "\n";
}
}
This gives me the output:
1 3 2 4 0 5 8 -3 0 6164240
1 4 6 0 3 5 8 -3 0 6164240
83 2 12 1 3 5 8 -3 0 6164240
30 4 -2 1 3 5 8 -3 0 6164240
Which is clearly wrong. I know this may have to do with me iterating over array elements that aren't initialized, but how can I avoid this error? Just as a note, it is essentially that I use arrays to hold the values at each line in the file. That is, one array must handle the integers in the first line; another in the second line...
Use this if you find it useful,
void readData(ifstream &inf)
{
string str;
vector<vector<int>> intArr;
while (getline(inf, str))
{
stringstream ss(str);
int i;
vector<int> a;
while (ss >> i)
{
a.push_back(i);
}
intArr.push_back(a);
}
}
You shouldn't use eof() to check end of file.
You can use
vector<vector<int> > main_array;
instead of an array - then just read items from input and push them (push_back) into the "inner" vector - this way you won't have uninitialized array elements at the end in case when data row is shorter than 10 elements.
Full solution may look like this (I've kept some parts of your original code for consistency):
using namespace std;
void read_input(ifstream &file)
{
vector<vector<int> > main_array;
string line; //buffer for single line read from file
// reading part
while(getline(file, line))
{
stringstream inStream(line);
vector<int> row; //single row buffer
int val = 0;
while(inStream >> val)
{
row.push_back(val);
}
main_array.push_back(row);
}
//display part
for(auto it = main_array.begin(), end = main_array.end(); it != end; ++it)
{
for(auto& s: *it)
{
cout << s << " ";
}
cout << "\n";
}
}
std::array is a container that encapsulates constant size arrays., so by declaring with array<int, 10> there are already 10 integers in the array. You need to use vector instead, and change your loop
while((inStream >> part_array[idx++]).good() == true);
to
bool good = true;
while(idx <10 ) {
int v;
if(!(inStream >> v).good() ) break;
idx++;
part_array.push_back(v); //now a vector
}
You have used an array with 10 elements but you know there may be fewer than ten items. It would be better to use a vector, and push_back what you have.
Aside from that, there are a variety of potential problems with the code. If you send it a bad ifstream it gets stuck in a loop.
while(!(file.eof())) is asking for trouble.
Next, file.ignore(); then ignores the first items of each line. If you change to use a vector of vectors as people have said, use while(getline(file, values)) instead of using the eof check and remove the ignore it should work.
Change this line
array<int, 10> part_array;
to
array<int, 10> part_array = {};