Parsing through text file with C++ - c++

I am trying to figure out the best way to read in numbers from a text file and set these numbers to variables. I am having trouble because there will be multiple text files that will be testing my code and they are all of different lengths and sizes. A sample test one looks like this:
0 (1,3) (3,5)
1 (2,6)
2 (4,2)
3 (1,1) (2,4) (4,6)
4 (0,3) (2,7)
Where the first number represents a vertex on a graph, the first number in a coordinate is the vertex it is going towards in a directed graph, and the second number is the weight of the edge. I tried doing getline and putting it into arrays but in certain test cases there could be 100 coordinates and I am not sure how to specify array size. I am also having trouble parsing through the parenthesis and comma and am not sure how to initialize the variables with the correct number from the text file.

Parsing shouldn't be that difficult, espacially when you can use std::stringstream to separate all the elements from input. Indeed, you want to remove all the paranthesis first then emplace the elements into the container.
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
#include <cctype>
int main()
{
std::ifstream read("file.txt");
std::vector<std::vector<std::pair<int, int>>> graph;
// read until you reach the end of the file
for (std::string line; std::getline(read, line); ) {
// removing punctuation like paranthesis, commas, etc.
std::replace_if(std::begin(line), std::end(line), [] (char x) { return std::ispunct(x); }, ' ');
// inserting the line into a stream that helps us parse the content
std::stringstream ss(line);
// read the node number
int source, destination, weight;
ss >> source;
// create a new vector for the new node, so you can place all it's destinations / weights in it
graph.insert(std::next(std::begin(graph), source), {{}});
// read the dests / weights until you reach the end of the current line
while (ss >> destination >> weight)
graph[source].emplace_back(destination, weight);
}
read.close();
std::ofstream write("output.txt");
for (const auto node : graph) {
for (const auto [dest, weight] : node)
write << "(" << dest << ", " << weight << ") ";
write << '\n';
}
}
Note that you need C++17 to compile the code. You have to use basic loops instead of ranged-for loops and omit auto if you use an older C++ standard. Also I used a vector of pairs, but it's better if you use a struct / class for nodes, to make the code more maintanable.

This works too.
#include<iostream>
#include<sstream>
#include<string>
using namespace std;
int main() {
int vertices[1000][3], qv = 0; //use number more than 1000 if it is required
while (cin) {
int n;
char c;
string s;
getline(cin, s);
istringstream is(s);
is >> n;
is >> c;
while (c == '(') {
vertices[qv][0] = n;
is >> vertices[qv][1];
is >> c; //,
is >> vertices[qv++][2];
is >> c; //)
is >> c; //(
}
}
for (int i = 0; i < qv; i++) //unified view
cout << vertices[i][0] << ' ' << vertices[i][1] << ' ' << vertices[i][2] << endl;
for (int i = 0; i < qv; i++) { //initial view
cout << vertices[i][0];
cout << " (" << vertices[i][1] << "," << vertices[i][2] << ")";
while (i + 1 < qv && vertices[i][0] == vertices[i + 1][0]) {
i++;
cout << " (" << vertices[i][1] << "," << vertices[i][2] << ")";
}
cout << endl;
}
}

I would use something like this:
#include <string>
#include <sstream>
#include <fstream>
#include <vector>
#include <algorithm>
struct coordinate
{
int vertex;
int weight;
};
struct vertex_set
{
int vertex;
std::vector<coordinate> coordinates;
};
std::istream& operator>>(std::istream &in, coordinate &out)
{
char ch1, ch2, ch3;
if (in >> ch1 >> out.to_vertex >> ch2 >> out.weight >> ch3)
{
if ((ch1 != '(') || (ch2 != ',') || (ch3 != ')'))
in.setstate(std::ios_base::failbit);
}
return in;
}
std::istream& operator>>(std::istream &in, std::vector<coordinate> &out)
{
out.clear();
coordinate coord;
while (in >> coord)
out.push_back(coord);
return in;
}
std::istream& operator>>(std::istream &in, vertex_set &out)
{
return in >> out.vertex >> out.coordinates;
}
std::ifstream f("file.txt");
std::string line;
while (std::getline(f, line))
{
vertex_set vs;
if (std::istringstream(line) >> vs)
{
// use vs.vertex and vs.coordinates as needed...
}
}

Related

C++ program to perform operations using a text file

item,price,qty,ordno,trdno
abc,54,2,123,32
xyz,34,2,345,21
item: string (char[])
price,qty (int)
ordno (long long)
trdno (int)
Make a structure for above mentioned fields
Make a vector (array, or any other container type) to hold multiple instances of this structure
1: Read file
2: read line, split values
3: initialize above mentioned structure object
4: add this object of structure to container
5: when whole file is read.. iterate over the container and print each elements values (serialno, orderno, tradeno, price, qty, item)
I tried this and it is not working-
#include<bits/stdc++.h>
using namespace std;
struct item {
string name;
double price;
int quantity;
int order_no;
int trd_no;
};
int main()
{
int n;cin>>n;
string str, T;
ifstream read("input.txt");
while(getline(read,str))
{
cout<<str<<endl;
}
stringstream X(str); // X is an object of stringstream that references the S string
cout<<endl;
while (getline(X, T, ','))
{
cout << T << endl; // print split string
}
read.close();
return 0;
}
For the code that you are showing, you misplaced just one '}' after the first while. So the code will read in the first while-loop all lines of the file and is then empty. And, then you try to split the lines. This can of course not work.
If you move the closing bracket to in front of read.close(); then your code will work. Like this:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
using namespace std;
struct item {
string name;
double price;
int quantity;
int order_no;
int trd_no;
};
int main()
{
int n; cin >> n;
string str, T;
ifstream read("input.txt");
while (getline(read, str))
{
cout << str << endl;
stringstream X(str); // X is an object of stringstream that references the S string
cout << endl;
while (getline(X, T, ','))
{
cout << T << endl; // print split string
}
}
read.close();
return 0;
}
Caveat: this will not work, if the source file contains the header row! You can read this and throw it away, if needed.
If we follow the instruction of your homework, line by line, and assume that the first line contains header rows, then we would do like the following.
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
struct Item {
std::string name;
double price;
int quantity;
long long order_no;
int trd_no;
};
int main()
{
std::vector<Item> items;
std::string str, T;
std::ifstream read("input.txt");
// Read first line and ignore it
std::getline(read, str);
while (std::getline(read, str))
{
std::istringstream X(str);
Item tempItem;
getline(X, T, ',');
tempItem.name = T;
getline(X, T, ',');
tempItem.price = std::stod(T);
getline(X, T, ',');
tempItem.quantity = std::stoi(T);
getline(X, T, ',');
tempItem.order_no = std::stoll(T);
getline(X, T, ',');
tempItem.trd_no = std::stoi(T);
items.push_back(tempItem);
}
read.close();
for (const Item& item : items)
std::cout << item.name << ' ' << item.price << ' ' << item.quantity << ' '
<< item.order_no << ' ' << item.trd_no << '\n';
}
And, with a little bit more advanced C++, where we especially keep data and methods in a class, we could do the following:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <iomanip>
#include <algorithm>
#include <iterator>
// The item
struct Item {
std::string name{};
double price{};
int quantity{};
long long order_no{};
int trd_no{};
// Overwrite extraction operator for easier stream IO
friend std::istream& operator >> (std::istream& is, Item& i) {
char c;
return std::getline(is >> std::ws, i.name, ',') >> i.price >> c >> i.quantity >> c >> i.order_no >> c >> i.trd_no;
}
// Overwrite inserter for easier output
friend std::ostream& operator << (std::ostream& os, const Item& i) {
return os << "Name: " << i.name << "\tPrice: " << i.price << "\tQuantity: " << i.quantity << "\tOrder no: " << i.order_no << "\tTRD no: " << i.trd_no;
}
};
// The Container
struct Data {
std::vector<Item> items{};
// Overwrite extraction operator for easier stream IO
friend std::istream& operator >> (std::istream& is, Data& d) {
// Read header line and ignore it
std::string dummy; std::getline(is, dummy);
// Delete potential old data
d.items.clear();
// Read all new data from file
std::copy(std::istream_iterator<Item>(is), {}, std::back_inserter(d.items));
return is;
}
// Overwrite inserter for easier output
friend std::ostream& operator << (std::ostream& os, const Data& d) {
std::copy(d.items.begin(), d.items.end(), std::ostream_iterator<Item>(os, "\n"));
return os;
}
};
int main()
{
// Open file and check, if it could be opened
if (std::ifstream sourceFileStream("input.txt"); sourceFileStream) {
// Define container
Data data{};
// Read and parse complete source file and assign to data
sourceFileStream >> data;
// Show result
std::cout << data;
}
else std::cerr << "\n\nError: Could not open source file:\n\n";
}

Reading Triangle Data Mesh With Object Files

I need to load in the vertices of a .obj file in c++. I just copied over the verts from the object I downloaded so I didn't have to worry about texture mapping or anything. I was able to separate it into a single line and get rid of the v but am not able to isolate the x,y,z coordinates so I can assign it to three separate variables.
main.cpp
// ConsoleApplication3.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include <iostream>
#include <memory>
#include <fstream>
#include <string>
using namespace std;
string pos1, pos2, pos3;
int main()
{
string line;
ifstream myfile("fox.txt");
if (myfile.is_open())
{
while (getline(myfile, line))
{
line.erase(std::remove(line.begin(), line.end(), 'v'), line.end());
cout << line << endl;
}
myfile.close();
}
else cout << "Unable to open file";
return 0;
}
fox.txt
v 10.693913 60.403057 33.765018
v -7.016389 46.160694 36.028797
v 9.998714 51.307644 35.496368
v -8.642366 49.095310 35.725204
A simple way to read in the line
v 10.693913 60.403057 33.765018
and separate it into 3 different variables is to first read in a char, then read in three doubles:
ifstream fin("fox.txt");
vector <vector<double>> data; // holds sets of coordinates
double a, b, c;
char v;
while(fin >> v >> a >> b >> c){
data.push_back({a, b, c});
}
If you wanted, you could also use std::stringstream to parse the input into doubles.
An easy way is to simply use std::stringstream and treat it like you would any other stream.
#include <sstream>
...
std::string pos1, pos2, pos3;
std::stringstream lineStream;
...
while (getline(myfile, line))
{
/* Make a string stream out of the line we read */
lineStream.str(line);
char skip; // Temp var to skip the first 'v'
lineStream >> skip >> pos1 >> pos2 >> pos3;
/* Reset error state flags for next iteration */
lineStream.clear();
}
Or you could avoid all that by using the >> operator on myfile directly.
std::string temp, pos1, pos2, pos3;
while (myfile >> temp >> pos1 >> pos2 >> pos3)
{
...
}
I'm going to figure you want to store this data in the likes of a std::vector. This is one way of doing it.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
const char* test_str = R"(
v 10.693913 60.403057 33.765018
v -7.016389 46.160694 36.028797
v 9.998714 51.307644 35.496368
v -8.642366 49.095310 35.725204
)";
struct data_item {
double x;
double y;
double z;
};
using data_set = std::vector<data_item>;
int main()
{
//std::ifstream myfile("fox.txt");
//if (!myfile.is_open()) {
// std::cout << "Unable to open file\n";
// return -1;
//}
std::stringstream as_file;
as_file << test_str;
data_set set;
for (; ;) {
std::string dummy;
data_item item;
as_file >> dummy >> item.x >> item.y >> item.z;
if (!dummy.size())
break;
set.push_back(item);
}
for (auto& item : set)
std::cout << item.x << " " << item.y << " " << item.z << std::endl;
return 0;
}
Don't do: using namespace std; It will save you a lot of headaches down the road. It also makes your code more readable to know stuff is out of the standard library.
When testing, it is sometimes more simple to use local data as I have with test_str. As pointed out in the comments, you could just let the stream do the conversion from text to doubles.
Note I've taken care of a failed file error in one place, the commented file stuff. Putting an else way down from the failure is not so clear and creates a large unneeded scope.

Read matrix from text file using vectors in C++

we have a matrix in text file like this with commas between numbers, but there is no comma at the end of each line.
1,2,3,4
7,8,2,1
3,4,5,6
7,2,1,3
I was trying to do this with a 2D array like this but it wasn't really working out because also size of matrix is unknown.
string array[4][4];
int id;
for (int i = 0; i < 4; i++) { // go through each line
for (int j = 0; j < 4; j++) {
getline(filein, numbers, ',');
array[i][j] = numbers;
cout << array[i][j] << endl;
}
}
I want to do that with using 2D vectors but I have no idea how to do that. Like after creating a vector with
vector<vector<string>> matrix;
Should I create one more vector inside the loops?
Use vector of vectors. Here is the commentary for each line:
std::vector<std::vector<int>> v; // declare vector of vectors
std::ifstream ifs("myfile.txt"); // open the file
std::string tempstr; // declare a temporary string
int tempint; // declare a temporary integer
char delimiter; // declare a temporary delimiter
while (std::getline(ifs, tempstr)) { // read line by line from a file into a string
std::istringstream iss(tempstr); // initialize the stringstream with that string
std::vector<int> tempv; // declare a temporary vector for the row
while (iss >> tempint) { // extract the numbers from a stringstream
tempv.push_back(tempint); // push it onto our temporary vector
iss >> delimiter; // read the , delimiter
}
v.push_back(tempv); // push the vector onto vector of vectors
}
The full source code is:
#include <iostream>
#include <fstream>
#include <vector>
#include <sstream>
#include <string>
int main() {
std::vector<std::vector<int>> v;
std::ifstream ifs("myfile.txt");
std::string tempstr;
int tempint;
char delimiter;
while (std::getline(ifs, tempstr)) {
std::istringstream iss(tempstr);
std::vector<int> tempv;
while (iss >> tempint) {
tempv.push_back(tempint);
iss >> delimiter;
}
v.push_back(tempv);
}
for (auto row : v) {
for (auto el : row) {
std::cout << el << ' ';
}
std::cout << "\n";
}
}
Use a double std::vector, read the file line by line, and parse the commas as well, and you are done. Every time you read a line, increase the vector's size by 1. You can do that with std::vector::resize().
Example, using your file as "matrix.txt":
#include <iostream>
#include <sstream>
#include <string>
#include <fstream>
#include <vector>
int main(void) {
std::vector<std::vector<int>> matrix;
std::ifstream infile("matrix.txt");
int a, b, c, d;
char comma;
while (infile >> a >> comma >> b >> comma >> c >> comma >> d)
{
//std::cout << a << " " << b << " " << c << " " << d << std::endl;
matrix.resize(matrix.size() + 1);
matrix[matrix.size() - 1].push_back(a);
matrix[matrix.size() - 1].push_back(b);
matrix[matrix.size() - 1].push_back(c);
matrix[matrix.size() - 1].push_back(d);
}
for(auto row: matrix) {
for(auto v: row) {
std::cout << v << " ";
}
std::cout << "\n";
}
return 0;
}
Output:
Georgioss-MacBook-Pro:~ gsamaras$ g++ -std=c++0x main.cpp
Georgioss-MacBook-Pro:~ gsamaras$ ./a.out
1 2 3 4
7 8 2 1
3 4 5 6
7 2 1 3

Saving file contents to variables through >> operator

I am trying to read a file and then put the contents (seperator by a space) into variables.
The file's contents are always similar per line. It looks like this:
(50,60) CIRCLE yellow 10
(100,160) CIRCLE red 20
These values are position, name, colour and diameter.
Here is what I've got so far:
#include <SFML/Graphics.hpp>
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <string>
int main()
{
sf::RenderWindow window(sf::VideoMode(800, 600), "Practicum week 3");
std::ifstream input("input.txt");
std::string name;
std::string line;
sf::Vector2f position;
sf::Color colour;
while (std::getline(input, line))
{
input >> position >> name;
}
while (window.isOpen()) {
sf::Event Event;
while (window.pollEvent(Event)) {
if (Event.type == sf::Event::Closed) { window.close(); }
}
// Start frame
window.clear();
// End frame & display contents
window.display();
}
return 0;
}
The problem here is that at the input >> position >> name; it gives me an error:
"Error: no operator '>>' matches these operands"
operand types are: std::ifstream >> sf::vector2f
I've seen a lot of solutions to this problem being #include missing but as you can see I've already got that covered.
Am I missing something?
The following examples will show you how to take advantage of extraction operator overloading, so that your objects can read values directly from a stream.
You need to overload the operator >> for your object Vector2f. This should look something like this:
istream& operator>>(istream& is, const Vector2f& v2f)
{
is >> v2f.data_member >> v2f.another_data_member;
return is;
}
For example, to read this format: (50,60) CIRCLE yellow 10, your overloaded operator should look like: *slightly in pseudo-code
istream& operator>>(istream& is, const Any_Object& any_obj_instantiation)
{
// input variables
int first_num, second_num;
char open_par, close_par, comma;
Circle circle;
Color color;
int last_num;
// extract ; it skips whitespaces
is >> open_par >> first_num >> comma >> second_num >> cirlce >> color >> last_num;
// failed input
if(!is) return is;
// wrong input format
if(open_par != '(' || close_par != ')' || comma != ','){
is.clear(ios_base::failbit);
return is;
}
// pass the input values to the object
any_obj_instantiation(Vector2f(first_num, second_num), cirlce, color, last_num);
return is;
}
The above code snippet could be used as a benchmark and adapted to the objects you want to populate directly with the input stream operator.
How about stringstream?
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
struct vec{
int x;
int y;
};
int main() {
//string s;
//vec v;
//stringstream ss;
//ss << "20 10 test";
//ss >> v.x >> v.y >> s;
//cout << v.x << " " << v.y << " " << s.c_str(); // 20 10 test
//#EDIT - simple parsing
vec v;
string s = "(2034,10)";
size_t comaPos = s.find_first_of(',');
string x = s.substr(1, comaPos-1);
string y = s.substr(comaPos+1, s.find_first_of(')')-comaPos-1);
v.x = stoi(x);
v.y = stoi(y);
cout << v.x << " " << v.y;
return 0;
}
Note: you can't stream "(20,10)" directly to sf::Vector2f, but you can stream just the coords, as I did above.

How to group values from the same line into a pair?

I am trying to write a program that reads a text file which has a data set in the form below and puts every 2 integers per line into a pair:
1 2
3 4
5 6
... and so on
Currently, this part of my main code reads the file line by line and converts the imported strings to integers one number at a time, but I am not sure how I would group 2 integers at a time in each line and put them in a pair. Ultimately, I want to add all the pairs to a single set.
while (getline(fs, line)) {
istringstream is(line);
while(is >> line) {
int j = atoi(line.c_str());
cout << "j = " << j << endl;}}
You could simply use std::pair, like this:
istringstream is(line);
pair<int, int> p;
is >> p.first;
is >> p.second;
cout << p.first << " " << p.second;
In next step you could use std::set<std::pair<int, int> > to acheive your goal of putting the pairs into single set.
Try this:
std::ifstream ifs{ "yourfile.txt" };
// read line by line
std::string line;
while(std::getline(ifs, line)) {
std::istringstream iss{ line }; // make a stream on the line
auto values = std::make_pair<int, int>(0, 0);
if(iss >> values.first >> values.second) { // check stream state
// after reading two ints
// (this skips spaces)
std::cout << "a=" << values.first << ", b=" << values.second << "\n";
} else {
throw std::runtime_error{ "bad data" };
}
// at this point, you have read a valid pair of ints into "values"
}
See the code comments for explanations.
Use pair available in 'utility' header, following is the sample code for the same.
while (getline(fs, line))
{
stringstream is;
is<<test;
int i, j;
is>>i;
is>>j;
pair<int, int> pr(i,j);
}
Following may help:
std::string line;
std::set<std::pair<int, int>> myset;
while (std::getline(fs, line)) {
std::istringstream is(line);
int a, b;
if (is >> a >> b) {
myset.insert({a, b});
} else {
throw std::runtime_error("invalid data");
}
}
Live example
To consider 1, 5 as identical as 5, 1, you may use:
struct unordered_pair_comp
{
bool operator () (const std::pair<int, int>& lhs, const std::pair<int, int>& rhs) const
{
return std::minmax(lhs.first, lhs.second) < std::minmax(rhs.first, rhs.second);
}
};
and so your myset becomes std::set<std::pair<int, int>, unordered_pair_comp> myset;
Live example
Something like this should work for your purposes.
while (getline(fs, line))
{
istringstream is(line);
int i, j;
is >> i >> j;
std::pair<int,int> my_pair = std::make_pair (i, j);
}
Dont make the imported strings into integers. Read the two strings from one line and concatenate them. Then make them integers. Like this:
#include <sstream>
#include <fstream>
#include <string>
#include <iostream>
using namespace std;
int main()
{
string line;
ifstream fs("input.txt");
while (getline(fs, line))
{
istringstream is(line);
string num1, num2;
while (is >> num1 >> num2)
{
num1 += num2;
int j = stoi(num1);
cout << "j = " << j << endl;
}
cin.get();
}
}