Hey I am trying to read in the following lines using a getline
(15,0,1,#)
(2,11,2,.)
(3,20,0,S)
I want to be able to just extract the integers as ints and the characters as char, but I have no idea how to only extract those.
It seems you could read off the separators, i.e., '(', ')', and ',' and then just use the formatted input. Using a simple template for a manipulator should do the trick nicely:
#include <iostream>
#include <sstream>
template <char C>
std::istream& read_char(std::istream& in)
{
if ((in >> std::ws).peek() == C) {
in.ignore();
}
else {
in.setstate(std::ios_base::failbit);
}
return in;
}
auto const open_paren = &read_char<'('>;
auto const close_paren = &read_char<')'>;
auto const comma = &read_char<','>;
int main()
{
int x, y, z;
char c;
std::istringstream in("(1, 2, 3, x)\n(4, 5, 6, .)");
if (in >> open_paren >> x
>> comma >> y
>> comma >> z
>> comma >> c
>> close_paren) {
std::cout << "x=" << x << " y=" << y << " z=" << z << " c=" << c << '\n';
}
}
Compare the value you get from getline()'s hexadecimal value, and run some if statements to compare to ASCII. That will tell you if you grabbed a number, letter, or symbol.
Related
I have a program that opens a file, reads from the file and I need to ignore the strings from the file and save only the integers into variables.
So far I have tried with file.ignore(' '), but it ignores only the first line.
Does anyone has an idea how I can fix this?
My code is:
ifstream file1;
int num1, num2, num3;
int intervalNumber1, intervalNumber2, intervalNumber3;
file1.open("file1.txt");
if (file1.fail()) {
cout << "Error" << endl;
exit(1);
} else {
if (file1.is_open()) {
file1 >> num1;
file1 >> num2;
file1 >> num3;
file1 >> intervalNumber1;
file1 >> intervalNumber2;
file1 >> intervalNumber3;
}
}
File1.txt
Number 40
Number1 34
Number2 100
Interval Number [20, 50]
Interval Number1 [60, 100]
Interval Number2 [110, 300]
The simple approach is to read the strings the same way you read the integers. Assuming the format of the file is always the same and there is one word in the first the lines and two words in the next three lines and numbers are enclosed in [] and seperated by , you can just read the strings and the [ and ] and , in a char:
#include <sstream>
#include <utility>
#include <iostream>
int main() {
std::stringstream ss{R"(Number 40
Number1 34
Number2 100
Interval Number [20, 50]
Interval Number1 [60, 100]
Interval Number2 [110, 300])"};
std::string dummy_string;
char dummy_char;
int number0=0;
int number1=0;
int number2=0;
std::pair<int,int> interval0;
std::pair<int,int> interval1;
std::pair<int,int> interval2;
ss >> dummy_string >> number0;
ss >> dummy_string >> number1;
ss >> dummy_string >> number2;
ss >> dummy_string >> dummy_string >> dummy_char >> interval0.first >> dummy_char >> interval0.second >> dummy_char;
ss >> dummy_string >> dummy_string >> dummy_char >> interval1.first >> dummy_char >> interval1.second >> dummy_char;
ss >> dummy_string >> dummy_string >> dummy_char >> interval2.first >> dummy_char >> interval2.second >> dummy_char;
std::cout << number0 << " " << number1 << " " << number2 << "\n";
std::cout << interval0.first << " " << interval0.second << "\n";
std::cout << interval1.first << " " << interval1.second << "\n";
std::cout << interval2.first << " " << interval2.second << "\n";
}
Output:
40 34 100
20 50
60 100
110 300
Reading from a file stream instead of the string stream works the same.
You should consider to use a std::vector instead of naming variables foo0,foo1, foo2 etc. Also consider to use a custom struct, for example if the numbers and the intervals belong together:
struct my_data {
int number;
int interval_start;
int interval_stop;
};
For this type you can then overload the input operator <<.
You might want to use a regular expression to solve this, if there is a known pattern.
If you just want to extract all numeric characters, using std::copy_if to another array (eg a string) would do the job.
Another direct and performant option is, to read the file content into an array, (eg std::string) and iterate over the content, checking for numbers.
std::string file_content (... load file content);
for(char& c : file_content) {
if (isdigit(c)) {
have_fun_with_digit(c);
}
}
Similar actions using regular expressions
i am using two classes std::regex (std regular expression class), std::smatch (match results class) and algorithm regex_search. To read lines from a file, I use the getNewLine function, which skips empty lines
std::regex ref
where I apply a pattern that is suitable for both cases NUMBER and [NUMBER, NUMBER]. In all cases, the numbers are placed in separate groups. Pattern: [ \\[](\\d+)[ ,]*(\\d+)?
std::smatch ref
which contains at position 0 the entire search string and further indices contain the found groups
ReadNumber function read line from file and get from line NUMBER or [NUMBER, NUMBER] match_results which are converted to int by match group
stoi ref function converts found string of digits to int
#include <iostream>
#include <regex>
#include <fstream>
using namespace std;
ifstream file1;
regex re("[ \\[](\\d+)[ ,]*(\\d+)?");
bool getNewLine(ifstream& f, string& s) {
while (getline(f, s))
if (!s.empty()) return true;
return false;
}
/*
* t1 and t2 pointers for numbers read from the file,
* if t2 is nullptr then only one number is needed
*/
void ReadNumber(int *n1, int *n2=nullptr) {
smatch m;
string newLine;
if (!getNewLine(file1, newLine))
exit(1);
//Checking if a string contains a match
if (!regex_search(newLine, m, re))
exit(1);
*n1 = stoi(m[1]);
cout << *n1;
if (n2) {
//Checking that the second group contains digits
if(!m[2].length())
exit(1);
*n2 = stoi(m[2]);
cout << " " << *n2;
}
cout << endl;
}
int main()
{
const int ArrSize = 3;
int Numbers[ArrSize] = { 0,0,0 };
pair<int, int> intervalNumber[ArrSize] = { {0,0},{0,0},{0,0} };
file1.open("c:\\file1.txt");
if (file1.fail()) {
cout << "Error" << endl;
exit(1);
}
for (int i = 0; i < ArrSize; i++)
ReadNumber(&Numbers[i]);
for (int i = 0; i < ArrSize; i++)
ReadNumber(&(intervalNumber[i].first), &(intervalNumber[i].second));
file1.close();
return 0;
}
results
40
34
100
20 50
60 100
110 300
I have a class basically representing a tuple (double x, doubly y) and I have overloaded the << operator so I can print the class. Now I want to do the same for >>, so that it only supports following formats:
x, (x) and (x,y).
I jave following code:
std::ostream & operator<< (std::ostream &output, tuple &c){
output << "(" << c.x << "," << c.y << ")" << endl;
return output;
}
std::istream & operator>> (std::istream & input, tuple &c){
// Check for following patterns: x, (x) or (x,y)
}
Can I loop through input and regex match? In that case how?
Also how could I test that it's actually working, something like this
std::cin >> "(10.2,5.5)"
or do I need to read from a file to test?
Edit:
The answer given did solve this problem, but I wanted to add a way to test it as it might be to use of someone other than me:
tuple x(6,2);
stringstream ss;
ss << x;
ASSERT_EQUALS(ss.str(), "(6,2)\n");
Regex would just be unnecessary for a simple input task such as this. Here is how I would do it, without any checking for valid input or not, just parsing:
std::istream & operator>> (std::istream & input, tuple &c){
// Check for following patterns: x, (x) or (x,y)
char firstCharacter;
input >> firstCharacter; // See what the first character is, since it varies
if (firstCharacter == '(')
{ // The simplest case, since the next few inputs are of the form n,n)
char delimiters;
input >> c.x >> delimiters >> c.y >> delimiters;
// N , N )
// You also here have to check whether the above inputs are valid,
// such as if the user enters a string instead of a number
// or if the delimeters are not commas or parentheses
}
else if (isdigit(firstCharacter) || firstCharacter == '-')
{ // For negative numbers
char delimiters;
input.unget(); // Put the number back in the stream and read a number
input >> c.x >> delimiters >> delimiters >> c.y >> delimiters;
// N , ( N )
// You also here have to check whether the above inputs are valid,
// such as if the user enters a string instead of a number
// or if the delimeters are not commas or parentheses
}
else
{
// Handle some sort of a parsing error where the first character
// is neither a parenthesis or a number
}
return input;
}
A little late to the party, but here is regex solution. While it is not pretty, it allows for negative numbers input as well as scientific notation. It also will tolerate spaces between numbers:
#include <iostream>
#include <regex>
#include <tuple>
std::istream &operator>> (std::istream &input, std::tuple<double, double> &t)
{
std::smatch m;
std::string s;
if (std::getline(input, s))
{
if (std::regex_match(s, m, std::regex(R"(\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)\s*)"))) //x
t = std::move(std::make_tuple(std::stod(m[1]), 0.0));
else if (std::regex_match(s, m, std::regex(R"(\s*\(\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)\s*\)\s*)"))) //(x)
t = std::move(std::make_tuple(std::stod(m[1]), 0.0));
else if (std::regex_match(s, m, std::regex(R"(\s*\(\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)\s*,\s*([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)\s*\)\s*)"))) //(x,y)
t = std::move(std::make_tuple(std::stod(m[1]), std::stod(m[2])));
}
return input;
}
int main()
{
std::tuple <double, double> t;
std::cout << "Enter data in format num, (num) or (num1,num2): ";
std::cin >> t;
std::cout << "Tuple 0: " << std::get<0>(t) << std::endl;
std::cout << "Tuple 1: " << std::get<1>(t) << std::endl;
return 0;
}
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...
}
}
I am trying to read complex numbers of the form x + y*i from a file which looks like this:
2 + 3i
4 + 5i
If I implement it like this it only works for the first line and I would like to be able to read again in the same manner the second line. Any ideas?
istream& operator>>(istream& in, Complex& c) {
in >> c.r >> c.i;
return in;
}
EDIT: Do not throw exceptions directly, as this is not the usual way of doing thing with iostreams.
EDIT: Process the sign character separately so that spaces in the input are allowed.
A quick and dirty solution:
istream& operator>>(istream& in, Complex& c)
{
char sign;
char suffix;
in >> c.r >> sign >> c.i >> suffix;
if ((sign != '+' && sign != '-') || suffix != 'i') {
in.setstate(ios::failbit);
}
if (sign == '-') {
c.i = -c.i;
}
return in;
}
You need to make sure that you read both the "+" and the "i" when processing the input.
The following implementation works:
struct complex
{
double r;
double i;
};
std::istream& operator>>(std::istream& in, complex& c)
{
char plus, i;
in >> c.r >> plus >> c.i >> i;
return in;
}
std::string initdata = "2 + 3i\n4 + 5i\n";
int main()
{
std::stringstream ss(initdata);
std::vector<complex> values;
std::istream_iterator<complex> begin(ss), end;
std::copy(begin, end, std::back_inserter<std::vector<complex>>(values));
}
I would do it like this :
#include <iostream>
struct complex
{
int r, i;
};
int main ()
{
complex co;
char c;
while ((std::cin >> co.r >> c /* '+' */ >> co.i >> c /* 'i' */))
{
std::cout << co.r << ' ' << co.i << "i\n";
}
}
This should work for you:
#include <iostream>
#include <sstream>
int main(int argc, char *argv[])
{
std::istringstream s(
"2 + 3i\n"
"4 + 5i\n"
);
char sgn;
double r;
double i;
while(s >> r >> sgn >> i) {
if(s.get() != 'i') {
std::cerr << "Missing i\n";
return -1;
}
std::cout << r << sgn << i << "i\n";
}
return 0;
}
Note: The space before the imaginary part and the trailing i are breaking the input.
I get this strange error. I think I have included all necessary files. What could cause this?
The error is caused by this line :
cin >> x >> "\n" >> y >> "\n";
Here is the code:
#ifndef ARITMETICE_H
#define ARITMETICE_H
#include <iostream>
#include <string>
#include "UI\comanda.h"
using namespace Calculator::Calcule;
namespace Calculator{
namespace UI{
template<int Operatie(int, int)>
class CmdAritmetice : public ComandaCalcule
{
public:
CmdAritmetice(const string &nume) : ComandaCalcule(nume)
{
}
void Execute()
{
cout << Nume() << "\n";
cout << "Introduceti doua numere intregi (x, y)\n";
int x, y;
cin >> x >> "\n" >> y >> "\n"; // here a get the error
cout << x << " " << Nume() << " " << y << " = " << Operatie (x,y) <<"\n";
}
};
}
}
#endif
The problem is cin >> "\n". It purports to read user input into a string literal, which doesn't make any sense. Just drop it, make it cin >> x >> y;
Not sure what you expected to happen when you tried to extract data from a stream into a string literal!
cin >> must has a writable variable in the right,
your
cin>>"\n"
redirect cin to a const char* type, which is read only
in a word, just use cin>> x >> y; and the iostream will do the rest for you.
cin is object of type istream. This class has overloaded operator >> to take input from console and put the value into given variable. The variable must be l-value, not r-value. In short, the expression given on right of >> must be writable variable.
This will not work:
const int x;
cin >> x;
Simply because x is a const int&, and not int& which what istream::operator>>(int&) expects.
Going this way further, when you make a call:
cin >> "\n";
You are essentially calling operator >> (const char*) and not operator >> (char*), and hence the error. Because of multiple overloads of operator>> and template code behind, the error is not clear.
Note: The exact signature of operator >> might differ, but problem is with const-ness.
If your intention is to consume white spaces and one new line character (note the are flavors of new line representations), you might write a manipulator
#include <cctype>
#include <iostream>
#include <sstream>
std::istream& nl(std::istream& is) {
typedef std::istream::traits_type traits;
while(true) {
traits::int_type ch;
if(std::isspace(ch = is.get())) {
if(ch == '\n') break;
}
else {
is.putback(ch);
// No space and no '\n'
is.setstate(std::ios_base::failbit);
break;
}
}
return is;
}
int main()
{
std::istringstream s("1\n2");
int x, y;
s >> x >> nl >> y >> nl;
std::cout << x << ' ' << y << '\n';
if(s.fail()) {
// The missing '\n' after '2'
std::cout <<"Failure\n";
}
return 0;
}