Properly handling input when file has less numbers than expected - c++

I'm attempting to read in a file with 3 floating point numbers per line. Right now, I have this implemented as:
std::ifstream inFile(inName.c_str());
if (!inFile) {
prterr("in ASCII file could not be opened!\n");
return -1;
}
std::vector<double> xData, yData, zData;
xData.resize(nPoints);
yData.resize(nPoints);
zData.resize(nPoints);
inFile.precision(std::numeric_limits<double>::digits10+1);
for (int i = 0; i < nPoints; ++i) {
inFile >> xData[i] >> yData[i] >> zData[i];
inFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
so that the program successfully runs even if the user inputs more than 3 numbers per line. However, sometimes a user tries to run the program with <3 numbers per line. When this happens, the parser will obviously store the data incorrectly.
I would like to either (a) throw an error if the file has less than 3 numbers per line, or (b) Only store the first N numbers per line in their respective vectors if only N numbers per line are present in the file. The trick is, I want to do this as quickly as possible, as my datasets can be several GBs. I can be guaranteed that my file has the exact same amount of numbers per line.
Is there a graceful and efficient way to perform (b)? I know I could implement (a) just by reading the first line as a string separately before the for loop, but that seems quite ugly. Is there a better way to do this?

I know you dd not want to read in the first line as a std::string but you need someway to find out how many white space separated columns there are and unfortunately a newline is treated like white space. If you are okay with doing that though then you can see how man columns you have with
std::ifstream inFile(inName.c_str());
std::vector<int> columns_in_file;
std::string temp;
std::getline(inFile, temp);
std::stringstream ss(temp);
int number;
while (ss >> number)
columns_in_file.push_back(number);
Then what we need to do is set up a 2d vector that will have the correct number of columns and rows.
// get number of columns. 3 max
int columns = columns_in_file.size() <= 3 ? columns_in_file.size() : 3;
std::vector<std::vector<int>> data(nPoints, std::vector<int>(columns));
// now we add the data we already read
for (int i = 0; i < columns; i++)
data[0][i] = columns_in_file[i];
Now we have a vector that is the same size as the file unless the file has more then 3 columns and has the first line of data in it. Now we have a decision to make, since you will only ever need to call
inFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
while reading if columns_in_file.size() > 3, then we don't want to call it if it is not needed. We could either have the reading code in two different functions or in two different blocks in an else if statement. The latter is what I will show but know you could refactor it into function calls. So to actually read the file we would have something like
if (columns <= 3)
{
for (int i = 0; i < nPoints; i++)
{
for(int j = 0; j < columns; j++)
{
infile >> data[i][j];
}
}
}
else
{
for (int i = 0; i < nPoints; i++)
{
for(int j = 0; j < columns; j++)
{
infile >> data[i][j];
inFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
}
}

You can first get the number of columns in the file while reading the first set of values,as a string, and then use the count with another loop inside the first for loop:
[EDIT] As per the comments given(and the learning continues again), instead of making the all vectors resize initially, you can resize them depending on the available columns. this will avoid unnecessary space consumption for the unused vectors.
std::vector<double> Data[3];//the x,y,z data set(Assuming the maximum number of columns can't be >3)
//you can decide which of the vectors(x,y,z) are used by looking at the column count
inFile.precision(std::numeric_limits<double>::digits10+1);
int count=0;//count the number of columns
string first_line;
double temp;
getline(inFile,first_line);
istringstream ss(first_line);
while(ss>>temp && count<3)
{
Data[count].resize(nPoints);
Data[count][0]=temp;
count++;
}
for(int i=1; i<nPoints&& inFile.peek() != EOF ; i++)
{
for(int j=0;j<count;j++)
{
inFile>>temp;
Data[j][i]=temp;
}
inFile.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

Related

Reading Column Specific Data in C++ [duplicate]

I have a text file that has values and I want to put them into a 2D vector.
I can do it with arrays but I don't know how to do it with vectors.
The vector size should be like vector2D[nColumns][nLines] that I don't know in advance. At the most I can have in the text file the number of columns, but not the number of lines.
The number of columns could be different, from one .txt file to another.
.txt example:
189.53 -1.6700 58.550 33.780 58.867
190.13 -3.4700 56.970 42.190 75.546
190.73 -1.3000 62.360 34.640 56.456
191.33 -1.7600 54.770 35.250 65.470
191.93 -8.7500 58.410 33.900 63.505
with arrays I do it like this:
//------ Declares Array for values ------//
const int nCol = countCols; // read from file
float values[nCol][nLin];
// Fill Array with '-1'
for (int c = 0; c < nCol; c++) {
for (int l = 0; l < nLin; l++) {
values[c][l] = -1;
}
}
// reads file to end of *file*, not line
while (!inFile.eof()) {
for (int y = 0; y < nLin; y++) {
for (int i = 0; i < nCol; i++) {
inFile >> values[i][y];
}
i = 0;
}
}
Instead of using
float values[nCol][nLin];
use
std::vector<std::vector<float>> v;
You have to #include<vector> for this.
Now you don't need to worry about size.
Adding elements is as simple as
std::vector<float> f; f.push_back(7.5); v.push_back(f);
Also do not use .eof() on streams, because it doesn't set it until after the end has been reached and so it will attempt to read the end of the file.
while(!inFile.eof())
Should be
while (inFile >> values[i][y]) // returns true as long as it reads in data to values[x][y]
NOTE: Instead of vector, you can also use std::array, which is apparently the best thing after sliced bread.
My suggestion:
const int nCol = countCols; // read from file
std::vector<std::vector<float>> values; // your entire data-set of values
std::vector<float> line(nCol, -1.0); // create one line of nCol size and fill with -1
// reads file to end of *file*, not line
bool done = false;
while (!done)
{
for (int i = 0; !done && i < nCol; i++)
{
done = !(inFile >> line[i]);
}
values.push_back(line);
}
Now your dataset has:
values.size() // number of lines
and can be adressed with array notation also (besides using iterators):
float v = values[i][j];
Note: this code does not take into account the fact that the last line may have less that nCol data values, and so the end of the line vector will contain wrong values at end of file. You may want to add code to clear the end of the line vector when done becomes false, before you push it into values.

Reading a list of names from a text File into a 2D array

I'm trying to read a column of names from a text file into a two dimensional array, the names vary in length but are at max 8 letters long, and there are 10 of them. Here are my two for loops to read the names and then to print them.
for (int i = 0; i != 10; i++) {
for (int j = 0; j != 8; j++) {
infile >> textfileinfo[i][j];
}
}
and then to print the names out I have this loop.
for (int i = 0; i != 10; i++) {
for (int j = 0; j != 8; j++) {
cout << textfileinfo[i][j];
}
cout << " " << endl;
}
Here is the list of names:
Victor
Eve
Juliet
Hector
Danielle
Romeo
Oscar
June
Ares
Dannae
What ends up happening is it will read the names out with 8 characters regardless, taking the extra characters from the next name, so Victor turns into VictorEv, then eJulietH and so on. How do I get it to start on the next row once the end of Victor is reached, and then move on to Eve etc. I am not allowed to use pointers either. Any help is appreciated! Thanks.
Seems the obvious thing is to use getline. getline is designed to read a single line of text.
for (int i = 0; i != 10; i++) {
infile.getline(textfileinfo[i], 9);
}
Note that the second argument to getline is 9 because you need an extra character to store the nul terminator that ends every C style string. This also mean that your 2D array need to be declared with at least 9 characters in each row. Slightly safer code would be to make this explicit
infile.getline(textfileinfo[i], sizeof textfileinfo[i]);
If for some reason you aren't allowed to use getline then you going to have to modify your inner loop to detect and deal with the '\n' character that terminates every line.

Read Strings from a text file and place in 2D Array

I've spent the past two hours search this and other forums for pointers & guidance on how I can read a txt file, line by line, and store them in a 2D array that I can manipulate with other functions and then later save the array back to the text file; with little luck. Hopefully some bright spark can point me in the right direction.
Currently my text file contains data, separated by spaces, that looks like this:
Number1 Number2 Number3
Colour1 Colour2 Colour3
Letter1 Letter2 Letter3
...
The "getting and setting" of the 2D Array needs to be done in a separate function, and I think the array's need to be global because they will later be manipulated by other functions (i.e. add a new row, delete a row, etc.)
I know the start to the function will need to look something like this:
void getAndSetData()
{
fstream file1;
file1.open("1.txt", ios::in);
}
And will contain a nested for loop that will in turn, set the elements in the 2D array 1 by one. However, I'm really at a loss as how to go about this.
Thank you in-advance.
Hello here is example code how I did it
ifstream in("test.txt"); // input file
string my_array[3][200];
void read_into_array()
{
if(in.is_open())
{
for(int i = 0; i < 200; ++i) // number of rows
{
for(int j = 0; j < 3; ++j) // number of columns
{
in >> my_array[i][j];
cout<<my_array[i][j]<<"\t";
}
cout<<endl;
}
}
}

How can I transfer a text file to array 2D in C++?

How can i transfer a text file(.txt) to array 2D in c++ and this is my source code
fstream fin('textfile.txt', ios::in);
int matrix[n][m]; // n is the number of rows and m is the number of columns
for(int i = 0;i < n; i++){
for(int j = 0; j<m; j++){
fin>>matrix[i][j];
}
}
but how can i detemine n and m for do this,i need your help and your advice, please Join us your perspectives
This solution requires C++11+
If there is no n & m in the file, you must assume that the layout is also in 2D
One Two Three
Four Five Six
Warning:: Untested code.
std::stringstream res;
std::string wordUp;
std::vector<std::string> str;
// the matrix, vector of vector of strings.
std::vector<std::vector<std::string>> matrix;
fstream fin('textfile.txt', ios::in);
int lines = 0;
int words = 0;
// read the file line by line using `getline`
for (std::string line; std::getline(fin, line); ) {
++lines;
// use stringstream to count the number of words (m).
res.str(line); // assign line to res. might also need some reset of good().
while (res.good()) {
res >> wordUp;
str.push_back(wordUp);
++words;
}
matrix.push_back(str);
str.erase(str.begin());
}
So u mean u want to read a file and copy contents to char Matrix[][]??, you can use while loop to read characters, and split every 1024 bytes(1 kb) by lines, i mean do this:
#include <fstream.h>
int main()
{
int n=0, m=0, MAX_LINES=1025, i=0, j=0;
char character, Matrix[1024][1025];
fstream file;
file.open("file.txt", ios::in);
while (file.get(character))// loop executes until the get() function is able to read contents or characters from the file
{
Matrix[n][m]=character; // copy the content of character to matrix[n][m], here n is used to work as lines and m as normal arrays.
m++; // finished copying to matrix[n][m] then m++ else the program will overwrite the contents to the same array.
If (m>=1024)// if string[n][m] reached the limit which is 1024.
{
Matrix[n][m]='\0'; //termimate that line
m=0;// start with a new column
if (n<MAX_LINES)// if n is less than 1024 then
n++;// write to a next line because m can support only 1024 chars.
}
}
Matrix[n][m]='\0';// this will terminate the whole string not just one line.
file.close();
for (i=0; i<1025; i++)
{
for (j=0; j<=1024 || Matrix[i][j]!='\0'; j++)
cout<<Matrix[i][j];
}
return 0;
}
This code will read 1024×1024 chars, but if the txt file is less than 1024(m) characters, the while loop will be exited and Matrix[n][m]='\0'; statement is executed.
EDIT: As asked by David i have written the whole code with main() sorry bro i forget, the bug in the code was the variables n and m were intialised to 1025 and 1024 so the program skips writing to Matrix as the Matrix[1024][1025] cannot store more characters... I think this would help, ok bro...

c++ How can I read values from a .csv file and store them in a vector?

I use Code::Blocks on Windows 7 to make little .exe from .cpp files, and I am a beginner (sorry!)
Here's today's problem:
I have a .csv file containing long integers (from 0 to 2^16) separated by semicolons and listed as a series of horizontal lines.
I will make a simple example here, but in reality the file can be up to 2Go big.
Let's say my file wall.csv appears like this in a text editor such as Notepad++:
350;3240;2292;33364;3206;266;290
362;314;244;2726;24342;2362;310
392;326;248;2546;2438;228;314
378;334;274;2842;308;3232;356
Strangely enough, it appears like this in the windows notepad
350;3240;2292;33364;3206;266;290
362;314;244;2726;24342;2362;310
392;326;248;2546;2438;228;314
378;334;274;2842;308;3232;356
Anyway,
let's say that I will know and will declare in 3 float variables the amount of columns, the amount of lines, and a value from the file.
int col = 7; // amount of columns
int lines = 4; // amount of lines
float x = 0; // a variable that will contain a value from the file
I want:
to create a vector <float> myValues
do myValues.push_back(x) with each value from the 1st line of the csv
do myValues.push_back(x) with each value from the 2nd line of the csv
do myValues.push_back(x) with each value from the 3rd line ...etc.
until the file has been entirely stored in the vector myValues.
My problem:
I don't know how to successively assign to the variable x the values present in the csv file.
How should I do that?
OK this code works (rather slowly but ok!):
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;
int col = 1221; // amount of columns
int lines = 914; // amount of lines
int x = 0; // a variable that will contain a value from the file
vector <int> myValues;
int main() {
ifstream ifs ("walls.tif.csv");
char dummy;
for (int i = 0; i < lines; ++i){
for (int i = 0; i < col; ++i){
ifs >> x;
myValues.push_back(x);
// So the dummy won't eat digits
if (i < (col - 1))
ifs >> dummy;
}
}
float A = 0;
float B = col*lines;
for (size_t i = 0; i < myValues.size(); ++i){
float C = 100*(A/B);
A++;
// display progress and successive x values
cout << C << "% accomplished, pix = " << myValues[i] <<endl;
}
}
Put the text data into a stringstream and use std::getline.
It takes an optional third parameter which is the "end-of-line" character, but you can use ; instead of a real end of line.
Call
while (std::getline(ss, str, ';')) {..}
and each loop puts the text in std::string.
Then you will need to convert to a number data type and push into a vector but this will get you started.
Also, why do you use floats for the number of columns and lines?
They are integer values.
Try using the C++ Standard Template Library's input operations.
Make a dummy character variable to eat up semicolons, then cin numbers into your x variable like so:
char dummy;
for (int i = 0; i < lines; ++i){
for (int i = 0; i < col; ++i){
cin >> x;
myValues.push_back(x);
// So the dummy won't eat digits
if (i < (col - 1))
cin >> dummy;
}
}
To do it this way, you can redirect your csv file to be input from the command line like so:
yourExecutable.exe < yourFile.csv
To loop through a vector that is filled with data:
for (size_t i = 0; i < myVector.size(); ++i){
cout << myVector[i];
}
Above, the size_t type is defined by the STL library and is used to suppress an error.
If you want to use the values only once, removing them from the container as they are used, you're better off using the std::queue container. This way, you look at the front element using front() and remove it using pop().