Read file and input into vector - c++

Given a text file with the following fixed contents (only two lines, nothing more):
43 65 123 13 41
83 67 22
I need it to turn into a vector array where I can apply the name.size() to find out the length of the array and also to make use of the integers inside for other purposes.
The result should be:
Array 1: 43 65 123 13 41
Array 2: 83 67 22
Array 1 Length: 5
Array 2 Length: 3
I have successfully read the text file and added the content using .push_back(). However when I cout the vector .size(), it returns 2 and I realised that it consider each line as an element in the array.
What is the approach to tackling this?
Edit:
Added snippet of the code:
vector <string> readFile(const string& fileName)
{
ifstream source;
source.open(filename);
vector <string> lines;
string line;
while(getline(source, line)
{
lines.push_back(line);
} return lines;
}
int main(int argc, char ** argv)
{
string inputFile(argv[1]);
vector <string> fileData = readFile(inputFile);
// Check vector
for(auto i : fileData)
cout << i << endl;
// Check vector length
cout << fileData.size() << endl;
}

As already mentioned, std::getline and std::istringstream are good starting points.
Then you can use std::istream_iterator to create the vectors directly using the iterator overload of the std::vector constructor.
Perhaps something like this:
std::vector<std::vector<int>> read_file(std::istream& input)
{
std::vector<std::vector<int>> lines;
std::string line;
while (std::getline(input, line))
{
std::istringstream line_stream(line);
lines.emplace_back(std::istream_iterator<int>(line_stream),
std::istream_iterator<int>());
}
return lines;
}
This handles an arbitrary number of lines containing an arbitrary number of integer values.
Can be used something like this:
int main(int, char* argv[])
{
std::ifstream input(argv[1]);
auto data = read_file(input);
std::cout << "First line of data: ";
for (auto value : data[0])
{
std::cout << value << ' ';
}
std::cout << '\n';
std::cout << "Second line of data: ";
for (auto value : data[1])
{
std::cout << value << ' ';
}
std::cout << '\n';
}
Important note: The example above lacks any kind of error or range checking. It's left as an exercise for the reader.

You could read each one as a text line, then extract the numbers from the text.
std::vector<std::vector<int>> database;
std::string text_line;
while (std::getline(cin, text_line))
{
std::vector<int> numbers_row;
std::istringstream number_stream(text_line);
int value;
while (number_stream >> value)
{
numbers_row.push_back(value);
}
database.push_back(numbers_row);
}
The inner loop creates a vector based on the numbers in that text line.
The outer loop creates vectors and appends them to the database.

The common way is to use std::getline to read line by line, to put it in a std::istringstream and then to use formatted input from that stream - but you've already gotten answers for that so I'll throw in an alternative.
This uses formatted input directly from the ifstream (or any istream descendant) and peek()s to see if a newline (\n) is the next character.
It could look like this:
#include <istream>
#include <vector>
std::vector<std::vector<int>> read_stream(std::istream& is) {
std::vector<std::vector<int>> res;
std::vector<int> cur;
int x;
while(true) {
if(is.peek() == '\n') {
// save what we've gotten on the current line
res.emplace_back(std::move(cur));
cur.clear();
}
if(is >> x) cur.push_back(x);
else break;
}
return res;
}

Related

Read a 3x3 array from a text file into a 2d vector with correct format

file.txt
13 14 15
10 17 152
1 34 56
string line;
ifstream infile("file.txt");
vector<vector<char> > num;
while (getline(infile, line)) {
vector<char> row;
for (char &c : line) {
if (c != ' ') {
row.push_back(c);
}
}
num.push_back(row);
}
for (vector<char> &row : num) {
for (char &c : row) {
cout << c << ' ';
}
std::cout << '\n';
}
I am new to C++ and i want some help on how to read the 3x3 array and store it into a 2d vector in the same format as the text file?
My current output looks like this:
1 3 1 4 1 5
1 0 1 7 1 5 2
1 3 4 5 6
Expected output:
13 14 15
10 17 152
1 34 56
It may help to change the way you are looking at the problem. Your use of getline() is fine, but when wanting to read multiple values from each line, you really need to think about it from the standpoint of what separates the values rather than the values themselves. It may be a spaces, as in this case, or a comma or a tab, etc... How you approach reading a delimited file is largely the same regardless of what the delimiters are.
Now in your case, having declared a std::vector<std::vector<char>>, you were bitten by the fact that the default for reading type char is to read one-byte, e.g. one ASCII character, regardless whether that character is a digit or alpha character. Since you want to read numbers containing multiple digits, the choice of char as the type will cause the problem described above. Instead, choose a type that expects multiple digits, such as int for your read and you will avoid this problem.
Now, back to reading the delimited file. The general approach is to read each line with getline() and then create a stringstream() from the line from which you can read individual values using the normal iostream operator. By creating a stringstream() you ensure that you only read values present in the line and that eliminates the problem where '\n' is simply ignored as whitespace.
For example in your case, you can pass an open std::ifstream along with a reference to a std::vector<std::vector<int>> as parameters to a function, then read each line, create a std::stringstream() from the line, and then fill a temporary vector of int with the values from the stringstream(). When done reading values into the temporary vector, you simply add that vector to your collection of vectors. You could do:
#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
bool read_vv_int (std::ifstream& infile, std::vector<std::vector<int>>& vv)
{
std::string line {}; /* string to hold line */
size_t szbegin = vv.size(); /* save beginning size of vv */
while (getline (infile, line)) { /* read each line */
std::stringstream ss {line}; /* create stringstream from line */
std::vector<int> v {}; /* create temp vector to fill */
int i; /* int to read from stringstream */
while (ss >> i) /* while int read from stringstream */
v.push_back(i); /* add to temp vector */
vv.push_back(v); /* add temp vector to vector of vector */
}
return szbegin != vv.size(); /* return true if vectors added */
}
(note: the function type bool is used to indicate success or failure of adding to your vector. If the size of your collection after your attempt at adding is the same as it was to begin with -- nothing was added, and the function returns false)
In main() to read your values into your collection you would simply open the file, validate the file is open for reading, declare your std::vector<std::vector<int>> and pass the open file stream along with a reference to your vector of vectors as parameters. On successful filling of your collection you can output them as follows:
int main (int argc, char **argv) {
if (argc < 2) { /* validate argument given for filename */
std::cerr << "error: insufficient input\n"
"usage: " << argv[0] << " filename\n";
return 1;
}
std::vector<std::vector<int>> vv {}; /* create vector of vector int */
std::ifstream infile(argv[1]); /* open file */
if (!infile.good()) { /* validate file open for reading */
std::cerr << "error: file open failed '" << argv[1] << "'.\n";
return 1;
}
if (read_vv_int (infile, vv)) /* if vectors successfully added */
for (const auto& r : vv) { /* loop over rows */
for (const auto& c : r) /* loop over cols */
std::cout << " " << std::setw(3) << c; /* output value */
std::cout.put('\n'); /* tidy up with newline */
}
}
(note: those are the two complete halves of the program which you can simply paste together and compile and test)
Example Use/Output
With your example data in the file dat/3x3.txt, you could read the file and generate the following output:
$ ./bin/read_2D_from_file dat/3x3.txt
13 14 15
10 17 152
1 34 56
Look things over and let me know if you have further questions.
Assuming you really want to store the numbers from the file, not the characters, I recommend you put each line into an std::istringstream and uses it to parse the numbers you would normally do from e.g. std::cin when you want to read plain int values.
Perhaps something like this:
std::vector<std::vector<int> > num;
std::string line;
while (std::getline(infile, line))
{
std::vector<int> values;
std::istringstream iss(line);
int value;
while (iss >> value)
{
values.push_back(value);
}
num.push_back(values);
}
With the input file as shown at the time of writing this answer, we should now have a 3x3 integer "matrix".
If we output it:
for (auto const& row : num)
{
for (auto value : row)
{
std::cout << value << ' ';
}
std::cout << '\n';
}
we should then get the output:
13 14 15
10 17 152
1 34 56
The inner while loop could be avoided if you use std::istream_iterator:
std::vector<std::vector<int> > num;
std::string line;
while (std::getline(infile, line))
{
std::istringstream iss(line);
std::vector<int> values(std::istream_iterator<int>(iss),
std::istream_iterator<int>());
num.push_back(values);
}
print like this remove ' ' this
for (vector<char> &row : num) {
for (char &c : row) {
cout << c;
}
std::cout << '\n';
}
This will work
while (getline(infile, line)) {
vector<char> row;
for (char &c : line) {
row.push_back(c);
}
num.push_back(row);
}
for (vector<char> &row : num) {
for (char &c : row) {
cout << c;
if(c==' ')
cout<<" ";
}
std::cout << '\n';
}

How to read in strings, based upon the first, and then third character, to a 2D string array in C++

So i have a text file of 35 lines, with the following format:
number [space] string [space] number [space] number [newline]
Now I wish to read the content of that text file into an array. But in a way that each line of the file is also represented in a separate array of its own. Thus creating a two-dimensional array.
Pseudo example:
myFileContentInArrayFormat = [
[1, "string", 155 29],
[2, "aa", 425 96],
[3, "blabla", 8375 2099]
]
My attempt is as follows and obviously does not read it in correctly.
void player::readToArray(ifstream& infile, string filename)
{
infile.open(filename);
for (int i = 0; i < 35; i++)
{
for (int e = 0; e < 13; e++)
{
infile >> Array[i][e];
}
}
}
I think I understand what you mean. You want to have all lines of your file into an array. But also want to be able to access each part - which are separated by a space - of that line by putting it into a vector.
Code:
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
//You probably want to take a look at vectors instead of arrays
#include <vector>
//readToArray
//:param filename: Name of the file to read from
std::vector< std::vector<std::string> > readToArray(std::string filename) {
//Declaring ifstream here, can be from parameter too like in your code but why would you do that?
std::ifstream file(filename);
std::string line;
std::vector<std::string> fileContent;
//Check if file succesfully opened
if (file.is_open()) {
//Read all the lines and put them in the vector
while (getline(file, line)) {
fileContent.push_back(line);
}
file.close();
} else {
std::cerr << "Error opening file " << filename << std::endl;
}
//Now all the lines are in the vector fileContent in string format
//Now create your two dimensional array (with the help of vectors)
std::vector< std::vector<std::string> > twoDimensionalVector;
//Loop over all lines
for (std::string& line : fileContent) {
//Splitting the contents of the file by space, into a vector
std::vector<std::string> inlineVector;
std::istringstream iss(line);
for(std::string s; iss >> s; )
inlineVector.push_back(s);
//Now push this separated line (which is now a vector to the final vector)
twoDimensionalVector.push_back(inlineVector);
}
//Return
return twoDimensionalVector;
}
int main() {
std::string filename = "test.txt";
std::vector< std::vector<std::string> > myVector = readToArray(filename);
//Now print out vector content
for (std::vector<std::string>& vec : myVector) {
//Print out vector content of the vector in myVector
std::cout << "[ ";
for (std::string& str : vec) {
std::cout << str << " ";
}
std::cout << "]" << std::endl;
}
}
test.txt
1 aaa 67 777
2 bbb 33 663
3 ccc 56 774
4 ddd 32 882
5 eee 43 995
You can access specific parts of your file by using the indexes of the vector. If you want to for example print out 'ddd' which is on line four the second part. you use:
//Access 'ddd' (line 4 second part)
std::cout << myVector[3][1] << std::endl;
Be aware of zero-indexing of course.

How to get a 2D array to write from a file with only spaces used as separators

I am attempting to take a file and put it into 3 different arrays. Two of these arrays are 1D arrays, and the other one is a 2D one. The text file is as follows
Bill Hansley 1 1 1 1 1
Todd Howard 2 3 1 0 0
Sam Duke 0 1 1 0 0
Danny Martin 1 0 2 0 1
I am trying to take this text file, and insert it the first names into an array called firstNames[], then another array for the last names called lastNames[], and finally for the numbers, I want them in an array called Productsorders[][]. My code is as follows.
bool loadOrderFile(string orderFN,
string firstNames[], string lastNames[],
int productsOrders[MAX_ORDERS][MAX_PRODS],
int &namesCount, int &prodCount, string &menuName)
{
ifstream File;
File.open(orderFN.c_str());
if (File.is_open()) {
cout << "Order file opened..." << endl;
}
int i = 0;
getline(File, menuName);
(File >> prodCount);
while (File) {
File.get();
(File >> firstNames[i]);
(File >> lastNames[i]);
(File >> productsOrders[i][i]);
(File >> productsOrders[i + 1][i + 1]);
(File >> productsOrders[i + 2][i + 2]);
(File >> productsOrders[i + 3][i + 3]);
(File >> productsOrders[i + 4][i + 4]);
(i++);
}
cout << "Menu name: " << menuName << endl;
cout << "Product Count: " << prodCount << endl;
cout << "There were " << (prodCount - 1) << " orders read in." << endl;
for (int i = 0; i < 10; i++) {
cout << productsOrders[i][i] << endl;
}
for (int i = 0; i < 10; i++) {
cout << firstNames[i] << lastNames[i] << endl;
}
return true;
}
The name arrays seem to work as they output the names as it should but the 2D array outputs
1
2
0
1
0
2
0
1
0
0
when it should be
1 1 1 1 1
2 3 1 0 0
0 1 1 0 0
1 0 2 0 1
I would appreciate any help.
Your problem is that you are not addressing your 2D arrays correctly here.
For example, in a 3x3 2D array you have two indexes [a][b], a 2D representation of that array would look like this:
[0][0] [0][1] [0][2]
[1][0] [1][1] [1][2]
[2][0] [2][1] [2][2]
So when you were outputting for example:
for (int i = 0; i < 10; i++) {
cout << productsOrders[i][i] << endl;
}
You can see that you would only be getting a diagonal line through the array rather than all of the items ([0][0], [1][1], [2][2]). To print the entire array you would need to use two loops.
You have a similar issue with your input where you are increasing both indexes simultaneously.
Honestly when parsing text files; I would separate the logic into individual functions according to their unique responsibilities.
You code would look something like this:
#include <vector>
#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <exception>
// Structure of your data type...
struct Order {
std::string firstName;
std::string lastName;
std::vector<int> productOrders; // maybe use vector instead of array...
// any other variable(s) or container(s) you may need...
};
// Simple ostream operator<< overload to print your Order Struct
// in a nice readable format.
std::ostream& operator<<(std::ostream& os, const Order& order );
// Function to split a string based on a single character delimiter
std::vector<std::string> splitString( const std::string& s, char delimiter );
// Function that will get each line text from a file and stores it into a vector
// of strings. Then closes the file handle once the entire file has been read.
void getAllLinesFromFile(const char* filename, std::vector<std::string>& output);
// This function will parse a single line of text or string that is contained in
// the vector of strings. The declaration of this can vary; if you have other
// information such as header information before the actual data structures
// you would have to modify this function's declaration-definition to accommodate
// for those variables.
void parseLine( const std::vector<std::string>& fileContents, std::vector<Order>& orders );
// Simple and clean looking main function.
int main() {
try {
std::vector<std::string> fileContents;
getAllLinesFromFile( "filename.txt", fileContents );
std::vector<Order> orders;
parseLine( fileContents, orders );
for ( auto& o : orders )
std::cout << o << '\n';
} catch( std::runtime_error& e ) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
std::ostream& operator<<(std::ostream& os, const Order& order ) {
os << order.firstName << " " << order.lastName << '\n';
for (auto& p : order.productOrders)
os << p << " ";
os << '\n';
return os;
}
std::vector<std::string> splitString( const std::string& s, char delimiter ) {
std::vector<std::string> tokens;
std::string token;
std::istringstream tokenStream( s );
while( std::getline( tokenStream, token, delimiter ) ) {
tokens.push_back( token );
}
return tokens;
}
void getAllLinesFromFile(const char* filename, std::vector<std::string>& output) {
std::ifstream file(filename);
if (!file) {
std::stringstream stream;
stream << "failed to open file " << filename << '\n';
throw std::runtime_error(stream.str());
} else {
std::cout << "File " << filename << " opened successfully.\n";
}
std::string line;
while (std::getline(file, line)) {
if (line.size() > 0)
output.push_back(line);
}
file.close();
}
void parseLine( const std::vector<std::string>& fileContents, std::vector<Order>& orders ) {
// Here is where you would do the logic to parse the vector of strings
// this function may vary based on your file structure. If there is any
// header information you would have to extract that first from the
// vector's index.
// Once you get to the index in the vector that describes your data structure
// it is hear that you would want to call `splitString()` using the current
// current index of that vector and the space character as your delimiter.
// This will create a vector of strings that are now considered to be tokens.
// On each pass of the loop for each line of contents you will want to
// create an instance of the Order Structure, then use that to populate
// the vector of Orders that was passed in by reference.
// Once all of the contents are done being parsed the function will exit
// and your vector of Orders will have the appropriate data.
}
Having this kind of structure would make it easier to debug when parsing text files. I believe that it is more elegant to retrieve all of the data from a file and store it into some container; the easiest to work with would be either a string, or some stream buffer such as stringstream. Then close the file when you are done getting all of the contents. After everything has been read from the text file and stored into a container of strings. This is where you would want to parse the strings or streams, and to check to see if the information is valid or not. Opening and closing a file handle many times is inefficient and slow, and much can go wrong. I think it's easier to get the contents from file, save it, close it and be done with the file, then move on to the actual work.

Need to parse a string of ints and get the white space

I have a file filled with ints (variable amount on a line), delimited by a space. I would like to parse out the int, then space, then int, then space ... until the newline char then start at a new line until the eof. An example file would look something like this:
1 1 324 234 12 123
2 2 312 403 234 234 123 125 23 34
...
To grab the ints I can do something like this:
std::ifstream inStream(file.txt);
std::string line;
int myInt = 0;
while(getline(inStream, line)) {
std::stringstream ss(line);
while(ss) {
ss >> myInt;
//process...
}
}
My question is that is there an easy way to also get the whitespace and endline char from the ss? Or is my best bet to write my program assuming a space after each index and a newline at the end of the ss? something like this:
std::ifstream inStream(file.txt);
std::string line;
int myInt = 0;
while(getline(inStream, line)) {
std::stringstream ss(line);
while(ss) {
ss >> myInt;
// process...
// done with myInt
char mySpace = ' ';
// now process mySpace
}
char myNewLine = '\n';
// now process myNewLine
}
If performance is not the most important issue, the following would be a general-purpose tokenizer for your input format. Whether this is a feasible solution depends of course on what you actually want to do with the input.
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
static void handle_number_string(std::string& literal) {
if (!literal.empty()) {
std::istringstream iss {literal};
int value;
if (iss >> value) {
std::clog << "<" << value << ">";
} else {
// TODO: Handle malformed integer literal
}
literal.clear();
}
}
int main(int argc, char** argv) {
for (int i = 1; i < argc; i++) {
std::string aux;
std::ifstream istr {argv[i]};
std::clog << argv[i] << ": ";
while (istr.good()) {
const int next = istr.get();
switch (next) {
case ' ':
handle_number_string(aux);
std::clog << "<SPC>";
break;
case '\n':
handle_number_string(aux);
std::clog << "<EOL>";
break;
default:
aux.push_back(next);
}
}
// Handle case that the last line was not terminated with '\n'.
handle_number_string(aux);
std::clog << std::endl;
}
return 0;
}
Addendum: I'd only do this if I absolutely had to. Handling all possibilities (multiple spaces, non-breaking spaces, tabs, \r\n,…) correctly will be a lot of work. If what you actually want to handle are the logical tokens field separator and end of line, manually parsing whitespace seems to be the wrong way to go. It would be sad if your program crashes just because a user has justified the columns in the input file (thus using a variable number of spaces).
Try something like this:
std::ifstream inStream(file.txt);
std::string line;
int myInt;
while (std::getline(inStream, line))
{
std::stringstream ss(line);
ss >> myInt;
if (ss)
{
do
{
// process...
// done with myInt
ss >> myInt;
if (!ss) break;
char mySpace = ' ';
// now process mySpace
}
while (true);
}
char myNewLine = '\n';
// now process myNewLine
}

How to read-write into/from text file with comma separated values

How do I read data from a file if my file is like this with comma separated values
1, 2, 3, 4, 5\n
6, 7, 8, 9, 10\n
\n
and after reading the file, I want to write the data back into other file as same format above.
I can get total number of lines, using
string line;
while(!file.eof()){
getline(file,line);
numlines++;
}
numline--; // remove the last empty line
but how can I know total number of digits in a row/line ??
I also have vector of ints to store the data.
So, I want to read the first line and then count total number of elements in that line, here 5 (1,2,3,4,5) and store them in array/vector, and read next line and store them in vector again and so on till I reach EOF.
Then, I want to write the data to file, again, I guess this will do the job of writing data to file,
numOfCols=1;
for(int i = 0; i < vector.size(); i++)
{
file << vector.at(i);
if((numOfCols<5) file << ",";//print comma (,)
if((i+1)%5==0)
{
file << endl;//print newline after 5th value
numOfCols=1;//start from column 1 again, for the next line
}
numOfCols++;
}
file << endl;// last new line
So, my main problem is how to read the data from file with comma separated values ??
Thanks
Step 1:
Don't do this:
while(!file.eof())
{
getline(file,line);
numlines++;
}
numline--;
The EOF is not true until you try and read past it.
The standard pattern is:
while(getline(file,line))
{
++numline;
}
Also note that std::getline() can optionally take a third parameter. This is the character to break on. By default this is the line terminator but you can specify a comma.
while(getline(file,line))
{
std::stringstream linestream(line);
std::string value;
while(getline(linestream,value,','))
{
std::cout << "Value(" << value << ")\n";
}
std::cout << "Line Finished" << std::endl;
}
If you store all the values in a single vector then print them out using a fixed width. Then I would do something like this.
struct LineWriter
{
LineWriter(std::ostream& str,int size)
:m_str(str)
,m_size(size)
,m_current(0)
{}
// The std::copy() does assignement to an iterator.
// This looks like this (*result) = <value>;
// So overide the operator * and the operator = to
LineWriter& operator*() {return *this;}
void operator=(int val)
{
++m_current;
m_str << val << (((m_current % m_size) == 0)?"\n":",");
}
// std::copy() increments the iterator. But this is not usfull here
// so just implement too empty methods to handle the increment.
void operator++() {}
void operator++(int) {}
// Local data.
std::ostream& m_str;
int const m_size;
int m_current;
};
void printCommaSepFixedSizeLinesFromVector(std::vector const& data,int linesize)
{
std::copy(data.begin(),data.end(),LineWriter(std::cout,linesize));
}
here I m posting the code for CSV read as well as write code. I have checked its working fine.
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
using namespace std;
void readCSV(istream &input, vector< vector<string> > &output)
{
string csvLine;
// read every line from the stream
while( getline(input, csvLine) )
{
istringstream csvStream(csvLine);
vector<string> csvColumn;
string csvElement;
// read every element from the line that is seperated by commas
// and put it into the vector or strings
while( getline(csvStream, csvElement, ',') )
{
csvColumn.push_back(csvElement);
}
output.push_back(csvColumn);
}
}
int main()
{
ofstream myfile;
string a;
fstream file("b.csv", ios::in);
myfile.open ("ab.csv");
if(!file.is_open())
{
cout << "File not found!\n";
return 1;
}
// typedef to save typing for the following object
typedef vector< vector<string> > csvVector;
csvVector csvData;
readCSV(file, csvData);
// print out read data to prove reading worked
for(csvVector::iterator i = csvData.begin(); i != csvData.end(); ++i)
{
for(vector<string>::iterator j = i->begin(); j != i->end(); ++j)
{
a=*j;
cout << a << " ";
myfile <<a<<",";
}
myfile <<"\n";
cout << "\n";
}
myfile.close();
system("pause");
}
Try the stringstream class:
#include <sstream>
Just in case, look up Stroustrup's "The C++ Programming Language Special Edition" page 641 example.
It works for me and I had a hell of a time trying to figure this one out.