fstream with a string delimiter or two char - c++

I have this situation where I need to get two int values from each row inside a file with this format:
43=>113
344=>22
Is it possible to do someting like setting a delimiter equal to => and than use >> operator to assign ints?
ifstream iFile("input.in");
int a,b;
iFile >> a >> b;
Also can be done autoamtically to output with similar format?
oFile << a << b;
instead of
oFile << a << "=>" << b;
Thanks.

You can't do it directly, without any extra code when reading or
writing, but you can write a manipulator which handles it for
you more explicitly:
std::istream&
mysep( std::istream& source )
{
source >> std::ws; // Skip whitespace.
if ( source.get() != '=' || source.get() != '>' ) {
// We didn't find the separator, so it's an error
source.setstate( std::ios_base::failbit );
}
return source;
}
Then, if you write:
ifile >> a >> mysep >> b;
, you will get an error is the separator is absent.
On output, you can use a similar manipulator:
std::ostream&
mysep( std::ostream& dest )
{
dest << "=>";
return dest;
}
This has the advantage of keeping the information as to what the
separator is isolated in these two specific functions (which
would be defined next to one another, in the same source file),
rather than spread out where ever you are reading or writing.
Also, these data presumably represent some particular type of
information in your code. If so, you should probably define it
as a class, and then defined operators >> and << over that
class.

Given a and b are variables of inbuilt types, you can not define your own user-defined operators for streaming them (the Standard library already provides such functions).
You could just write out code with the behaviour you want...
int a, b;
char eq, gt;
// this is probably good enough, though it would accept e.g. "29 = > 37" too.
// disable whitespace skipping with <iomanip>'s std::noskipws if you care....
if (iFile >> a >> eq >> gt >> b && eq == '=' && gt == '>')
...
OR wrap a and b into a class or struct, and provider user-defined operators for that. There are plenty of SO questions with answers explaining how to write such streaming functions.
OR write a support function...
#include <iomanip>
std::istream& skip_eq_gt(std::istream& is)
{
char eq, gt;
// save current state of skipws...
bool skipping = is.flags() & std::ios_base::skipws;
// putting noskipws between eq and gt means whatever the skipws state
// has been will still be honoured while seeking the first character - 'eq'
is >> eq >> std::noskipws >> gt;
// restore the earlier skipws setting...
if (skipping)
is.flags(is.flags() | std::ios_base::skipws);
// earlier ">>" operations may have set fail and/or eof, but check extra reasons to do so
if (eq != '=' || gt != '>')
is.setstate(std::ios_base::failbit)
return is;
}
...then use it like this...
if (std::cin >> a >> skip_eq_gt >> b)
...use a and b...
This function "works" because streams are designed to accept "io manipulator" functions that reconfigure some aspect of the stream (for example, std::noskipws), but for a function to be called it just has to match the prototype for an (input) io manipulator: std::istream& (std::istream&).

If you have always have => as the deliminator, you can write a function that will parse lines of the document.
void Parse(ifstream& i)
{
string l;
while(getline(i,l))
{
//First part
string first = l.substr(0, l.find("=>"));
//Second part
string second = l.substr(l.find("=>")+2, l.length());
//Do whatever you want to do with them.
}
}

Related

C++: how to input values separated by comma(,)

int a, b, c, d;
There are 4 variables.
I want user to input 4 values, and each value is separated by comma(,)
Just like this:
stdin:
1,2,3,4
The following code works in C
scanf("%d,%d,%d,%d", &a, &b, &c, &d);
But how should I code in C++?
I’m kind of surprised at the incorrect commentary here[1].
There are two basic routes you can take:
handle the separator with a manipulator-style object, or
imbue the stream with a specialized facet that requires whitespace to include a comma.
I will focus on the first; it is typically a bad idea to imbue shared streams with weird behaviors even temporarily (“shared” in the sense that other parts of your code have access to it as well; a local stringstream would be an ideal candidate for imbuing with specialized behaviors).
A ‘next item must be a comma’ extractor:
#include <cctype>
#include <iostream>
struct extract
{
char c;
extract( char c ): c(c) { }
};
std::istream& operator >> ( std::istream& ins, extract e )
{
// Skip leading whitespace IFF user is not asking to extract a whitespace character
if (!std::isspace( e.c )) ins >> std::ws;
// Attempt to get the specific character
if (ins.peek() == e.c) ins.get();
// Failure works as always
else ins.setstate( std::ios::failbit );
return ins;
}
int main()
{
int a, b;
std::cin >> a >> extract(',') >> b;
if (std::cin)
std::cout << a << ',' << b << "\n";
else
std::cout << "quiznak.\n";
}
Running this code, the extract manipulator/extractor/whatever will succeed only if the next non-whitespace item is a comma. It fails otherwise.
You can easily modify this to make the comma optional:
std::istream& operator >> ( std::istream& ins, optional_extract e )
{
// Skip leading whitespace IFF user is not asking to extract a whitespace character
if (!std::isspace( e.c )) ins >> std::ws;
// Attempt to get the specific character
if (ins.peek() == e.c) ins.get();
// There is no failure!
return ins;
}
...
std::cin >> a >> optional_extract(',') >> b;
Etc.
[1] cin >> a >> b; is not equivalent to scanf( "%d,%d", ...);. C++ does not magically ignore commas. Just as in C, you must treat them explicitly.
The same for the answer using getline() and a stringstream; while the combination is valid, the actual problem is just shifted from std::cin to another stream object, and still must be treated.

Is it possible to 'throw away' read value from input stream?

I process some data with data in columns like,
1 -0.004002415458937208 0.0035676328502415523
2 -0.004002415796209478 0.0035676331876702957
....
I am only interested in the last two values. I usually find it convenient to read the values as:
std::ifstream file(file_name);
double a, b;
for (lines) {
// | throwing away the first value by reading it to `a`
file >> a >> a >> b;
store(a, b);
}
I'm not sure how readable this is for others, and it might be thought of as an error when the structure of the data is not known. Could I somehow make it look more explicit that I really want to throw away the first read value?
I wanted something in the line of this, but nothing worked:
file >> double() >> a >> b; // I hoped I could create some r-value kind of thing and discard the data in there
file >> NULL >> a >> b;
If you don't want to create a variable explicitly to be ignored, and you feel explicitly ignoring the value with calls to manipulate the stream are too verbose, you could take advantage of the operator>> overload for std::istream that takes a std::istream&(*)(std::istream&) function pointer:
template <typename CharT>
std::basic_istream<CharT>& ignore(std::basic_istream<CharT>& in){
std::string ignoredValue;
return in >> ignoredValue;
}
to be used like:
std::cin >> ignore >> a >> b;
and if you wanted to verify that it was of a form that could be read into a type you could supply an additional template argument specifying the type of ignored value:
// default arguments to allow use of ignore without explicit type
template <typename T = std::string, typename CharT = char>
std::basic_istream<CharT>& ignore(std::basic_istream<CharT>& in){
T ignoredValue;
return in >> ignoredValue;
}
to be used like:
std::cin >> ignore >> a >> b;
// and
std::cin >> ignore<int> >> a >> b;
demo on coliru
You can use std::istream::ignore.
E.g.:
file.ignore(std::numeric_limits<std::streamsize>::max(), ' '); //columns are separated with space so passing it as the delimiter.
file >> a >> b;
You can use file::ignore(255, ' ') to ignore characters until the next space.
std::ifstream file(file_name);
double a, b;
for (lines) {
// skip first value until space
file.ignore(255, ' ');
file >> a >> b;
store(a, b);
}
or you can use an auxiliary variable to store the first value:
std::ifstream file(file_name);
double aux, a, b;
for (lines) {
// skip first value
file >> aux >> a >> b;
store(a, b);
}

How to obtain certain information from a specified format in c++?

Beginner programmer here,
Say I want to obtain initial starting coordinates in the form (x,y), so I ask the user to enter in a point using the specific form "(x,y)". Is there a way that I could recognize the format and parse the string so that I could obtain the x and y values?
Read a line of text using:
char line[200]; // Make it large enough.
int x;
int y;
fgets(line, sizeof(line), stdin);
Then, use sscanf to read the numbers from the line of text.
if ( sscanf(line, "(%d,%d)", &x, &y) != 2 )
{
// Deal with error.
}
else
{
// Got the numbers.
// Use them.
}
If you want to use iostreams instead of stdio, use getline instead of fgets.
You can use regex to find the matching sequence anywhere in the input.
#include <iostream>
#include <string>
#include <regex>
int main() {
std::string line;
std::getline(std::cin, line);
std::regex e(R"R(\(([-+]?(?:\d*[.])?\d+)\s*,\s*([-+]?(?:\d*[.])?\d+)\))R");
std::smatch sm;
if (std::regex_search(line, sm, e)) {
auto x = std::stod(sm[1]);
auto y = std::stod(sm[2]);
std::cout << "Numbers are: " << x << ", " << y << std::endl;
}
return 0;
}
In order to parse stuff you need a parser. There are many ways to write a parser, but generally parsers read tokens and decide what to do next based on which token it is. (Emphasized words are important, look them up).
In your case you don't have to explicitly introduce a separate token entity. Reading elements from the input stream with the >> operator will do.
You need to:
read a character; verify it's a '('
read a number
read a character; verify it's a ','
read a number
read a character; verify it's a ')'
If any step fails, the entire parsing fails.
You can see the same basic step is done three times, so you can write a function for it.
bool expect_char(std::istream& is, char what)
{
char ch;
return is >> ch && ch == what;
}
This works because is >> ch returns the stream after the read operation, and the stream can be viewed as a boolean value: true if the last operation succeeded, false otherwise.
Now you can compose your parser:
bool get_vector (std::istream& is, int& x, int& y)
{
return expect_char(is, '(') &&
is >> x &&
expect_char(is, ',') &&
is >> y &&
expect_char(is, ')');
}
This method has a nice property that blanks are allowed between the numbers and the symbols.
Now this may look like a lot of stuff to type compared to the solution that uses sscanf:
bool get_numbers2 (std::istream& is, int& x, int& y)
{
std::string s;
return std::getline(in, s) &&
(std::sscanf(s.c_str(), "(%d,%d)", &x, &y) == 2);
}
But sscanf is:
dangerous (there is no typechecking of its arguments)
less powerful (the input format is very rigid)
not generic (doesn't work in a template)
It's OK to use the scanf functions family where appropriate, but I don't recommend it for new C++ programmers.

Read at once inputs string and integer separated by a space in c++

I was wondering if, like in C, in C++ there was a way to take at once two different type of varibales as inputs.
For example I am supposed to read a command (a string) followed by an index(an integer) from a user, and they separated by a space.
The >> operator for a std::istream is overloaded for many different types. Any class which inherits >> from std::istream can read inputs from all the types >> is overloaded for. This means you can use the >> operator with std::cin, std::ifstream, std::istringstream, etc. to read in multiple types.
The basic syntax for your case would have the form
std::string s;
int n;
std::cin >> s >> n; // or std::ifstream, etc.
Of course you should perform error checking to make sure you actually received what you expected. You can use the ! operator to check if the stream is (not) in a good state. So in your case you might use the following:
std::string s;
int n;
if (!std::cin) {
// error handling here
} else {
std::cin >> s; // get the std::string
}
if (!std::cin) {
// error handling here
} else {
std::cin >> n; // get the int
}

How to use istringstream with more than one delimiter

Hi I'd like to ask how to parse multiple floats, separated by "/" and spaces, from a string.
The text format from the file is "f 1/1/1 2/2/2 3/3/3 4/4/4"
I need to parse every integer from this line of text into several int variables, which are then used to construct a "face" object(see below).
int a(0),b(0),c(0),d(0),e(0);
int t[4]={0,0,0,0};
//parsing code goes here
faces.push_back(new face(b,a,c,d,e,t[0],t[1],t[2],t[3],currentMaterial));
I could do it with sscanf(), but I've been warn away from that by my uni lecturer, so I am looking for an alternative. I am also not allowed other 3rd party libraries, including boost.
Regular expressions and parsing with stringstream() have been mentioned, but I don't really know much about either, and would appreciate some advice.
If you're reading the file with std::ifstream, there's no need for std::istringstream in the first place (although using the two is very similar because they inherit from the same base class). Here's how to do it with std::ifstream:
ifstream ifs("Your file.txt");
vector<int> numbers;
while (ifs)
{
while (ifs.peek() == ' ' || ifs.peek() == '/')
ifs.get();
int number;
if (ifs >> number)
numbers.push_back(number);
}
Taking into account your example f 1/1/1 2/2/2 3/3/3 4/4/4 what you need to read is: char int char int char int int char int char int int char int char int
To do this:
istringstream is(str);
char f, c;
int d[12];
bool success = (is >> f) && (f == 'f')
&& (is >> d[0]) && (is >> c) && (c == '/')
&& (is >> d[1]) && (is >> c) && (c == '/') &&
..... && (is >> d[11]);
The way I would do this is to change the interpretation of space to include the other separators. If I were to get fancy I would use different std::ostream objects, each with a std::ctype<char> facet set up to deal with one separator, and use a shared std::streambuf.
If you want to make the use of separators explicit you could instead use a suitable manipulator to skip the separator or, if it absent, indicate failure:
template <char Sep>
std::istream& sep(std::istream& in) {
if ((in >> std::ws).peek() != std::to_int_type(Sep)) {
in.setstate(std::ios_base::failbit);
}
else {
in.ignore();
}
return in;
}
std::istream& (* const slash)(std::istream&) = Sep<'/'>;
The code isn't tested and type on a mobile device, i.e., probably contains small errors. You'd read data like this:
if (in >> v1 >> v2 >> slash >> v3 /*...*/) {
deal_with_input(v1, v2, v3);
}
Note: the above use assumes input as
1.0 2.0/3.0
i.e. a space after the first value and a slash after the second value.
You can use boost::split.
Sample example is:
string line("test\ttest2\ttest3");
vector<string> strs;
boost::split(strs,line,boost::is_any_of("\t"));
cout << "* size of the vector: " << strs.size() << endl;
for (size_t i = 0; i < strs.size(); i++)
cout << strs[i] << endl;
more information here:
http://www.boost.org/doc/libs/1_51_0/doc/html/string_algo.html
and also related:
Splitting the string using boost::algorithm::split