I'm reading a file with C++; the file looks like:
tag1 2345
tag2 3425
tag3 3457
I would like to have something like
input>>must_be("tag1")>>var1>>must_be("tag2")>>var2>>must_be("tag3")>>var3;
Where everything blows up if what's being taken in doesn't match the argument of must_be() and, when done, var1=2345, var2=3425, var3=3457.
Is there a standard way of doing this? (Hopefully where "tag1" need not necessarily be a string, but this is not a requirement.) fscanf from C made it quite easy.
Thanks!
To clarify, each >> reads in one whitespace-delimited set of characters from input. I want to match some of the in-coming blocks of characters (tagX) against strings or data I have specified.
You need to implement operator>> for your class. Something like this :
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
struct A
{
A(const int tag_):tag(tag_),v(0){}
int tag;
int v;
};
#define ASSERT_CHECK( chk, err ) \
if ( !( chk ) ) \
throw std::string(err);
std::istream& operator>>( std::istream & is, A &a )
{
std::string tag;
is >> tag;
ASSERT_CHECK( tag.size() == 4, "tag size" );
std::stringstream ss(std::string(tag.begin()+3,tag.end()));
int tagVal;
ss >> tagVal;
std::cout<<"tag="<<tagVal<<" a.tag="<<a.tag<<std::endl;
ASSERT_CHECK( a.tag == tagVal,"tag value" );
is >> a.v;
return is;
}
int main() {
A a1(1);
A a2(2);
A a3(4);
try{
std::fstream f("in.txt" );
f >> a1 >> a2 >> a3;
}
catch(const std::string &e)
{
std::cout<<e<<std::endl;
}
std::cout<<"a1.v="<<a1.v<<std::endl;
std::cout<<"a2.v="<<a2.v<<std::endl;
std::cout<<"a3.v="<<a3.v<<std::endl;
}
Take a note that for wrong tag value, an exception will be thrown (meaning the tag much match).
Can't you read it line by line, and matching tags for each line? If the tag doesn't match what you expect you just skip the line and move on to the next.
Something like this:
const char *tags[] = {
"tag1",
"tag2",
"tag3",
};
int current_tag = 0; // tag1
const int tag_count = 3; // number of entries in the tags array
std::map<std::string, int> values;
std::string line;
while (current_tag < tag_count && std::getline(input, line))
{
std::istringstream is(line);
std::string tag;
int value;
is >> tag >> value;
if (tag == tags[current_tag])
values[tag] = value;
// else skip line (print error message perhaps?)
current_tag++;
}
Related
Okay, so i have a file that has a string like so:
10/11/12 12:30 PM,67.9,78,98
...
...
I want to separate it like so
10/11/12
12:30 PM
67.9
I know you use getline to separate the comma separated stuff:
getline(infile, my_string, ',')
but I also know that doing this to get the date:
getline(infile, my_string, ' ')
would read in the spaces into my_string
so is there any other way to go about this?
Also, what would I need to do to skip over the last 2 (78,98) and go to the next line? Would just a getline(infile, my_string) suffice?
You can read the string with getline and then use sscanf to read the formatted string :)
Consider using boost libraries that supplement the STL http://www.boost.org/doc/libs/1_57_0/doc/html/string_algo/usage.html
Give your stream a facet that interprets commas as whitespace (which will be our delimiter). Then just make a class that overloads the operator>>() function and leverages this new functionality. istream::ignore is the function use when you want to skip characters.
#include <iostream>
#include <vector>
#include <limits>
struct whitespace : std::ctype<char> {
static const mask* get_table() {
static std::vector<mask> v(classic_table(), classic_table() + table_size);
v[','] |= space; // comma will be classified as whitespace
v[' '] &= ~space; // space will not be classified as whitespace
return &v[0];
}
whitespace(std::size_t refs = 0) : std::ctype<char>(get_table(), false, refs) { }
};
template<class T>
using has_whitespace_locale = T;
struct row {
friend std::istream& operator>>(has_whitespace_locale<std::istream>& is, row& r) {
std::string temp;
is >> r.m_row >> temp;
r.m_row += temp;
is.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // skip the rest of the line
return is;
}
std::string get_row() const { return m_row; }
private:
std::string m_row;
};
// Test
#include <sstream>
#include <string>
int main() {
std::stringstream ss("10/11/12 12:30 PM,67.9,78,98\n4/24/11 4:52 AM,42.9,59,48");
std::cin.imbue(std::locale(std::cin.getloc(), new whitespace));
row r;
while (ss >> r) {
std::cout << r.get_row() << '\n';
}
}
Coliru Demo
in C++ I am creating a program that asks a user for a date in the following format: MM/DD/YYYY. Since the date is an int and must be an int, I figured the most logical way to get this in one line is if I was to use an array.
So I created something like this...
int dateArray[3];
for (int i=0; i<3; i++)
cin >> dateArray[i];
int month = dateArray[0];
...etc
My question is if a user enters "1/23/1980" is there a way I can ignore the / that the user inputs?
Thank you.
You can ignore one character using std::istream::ignore(). Since you probably only want to ignore intervening characters, you'd need to know when to ignore and when not to ignore. For a date I would personally not bother but just read the three terms:
if (((std::cin >> month).ignore() >> year).ignore() >> day) {
// do something with the date
}
else {
// deal with input errors
}
I would actually also be inclined to check that the correct separator is received and probably just create a manipulator for this purpose:
std::istream& slash(std::istream& in) {
if ((in >> std::ws).peek() != '/') {
in.setstate(std::ios_base::failbit);
}
else {
in.ignore();
}
return in;
}
// ....
if (std::cin >> month >> slash >> year >> slash >> day) {
// ...
}
... and, obviously, I would check in all cases that the input is correct.
Consider using C++11 regular expression library support for this type of parsing. For instance
#include <iostream>
#include <iterator>
#include <regex>
#include <string>
int main()
{
std::string string{ "12/34/5678" };
std::regex regex{ R"((\d{2})/(\d{2})/(\d{4}))" };
auto regexIterator = std::sregex_iterator( std::begin( string ), std::end( string ), regex );
std::vector< std::string > mdy;
for( auto matchItor = regexIterator; matchItor != std::sregex_iterator{}; ++matchItor )
{
std::smatch match{ *matchItor };
mdy.push_back( match.str() );
}
const std::size_t mdySize{ mdy.size() };
for( std::size_t matchIndex{ 0 }; matchIndex < mdySize; ++matchIndex )
{
if( matchIndex != mdySize && matchIndex != 0 ) std::cout << '/';
std::cout << mdy.at( matchIndex );
}
}
I would not ignore it; it's part of your format, even though you do not need to keep it around indefinitely.
I would read it into a char and make sure that it is, in fact, a /.
I'm trying to determine the best way to read in a configuration file. This "Parameters.cfg" file is just to define values and is of this form:
origin_uniform_distribution 0
origin_defined 1
angles_gaussian 0
angles_uniform_distribution 0
angles_defined 0
startx 0
starty 0
gap 500
nevents 1000
origin_uniform_distribution_x_min -5
origin_uniform_distribution_x_max 5
origin_uniform_distribution_y_min -5
origin_uniform_distribution_y_max 5
origin_defined_x 0
origin_defined_y 0
angles_gaussian_center 0
angles_gaussian_sigma 5
angles_uniform_distribution_x_min -5
angles_uniform_distribution_x_max 5
angles_uniform_distribution_y_min -5
angles_uniform_distribution_y_max 5
angles_defined_x 10
angles_defined_y 10
The names are there for the user to know which variables they are defining. I would like to have my program read in only the actual numbers and skip over the strings. I know I can do this in a way where I define a whole lot of strings in my program, and then just leave them sitting there defined but obviously unused. Is there a way to read in the numbers easily while skipping over the strings?
What's wrong with the obvious solution?
string param_name;
int param_value;
while ( fin >> param_name >> param_value )
{
..
}
You can discard the param_name after each iteration while storing the param_value wherever you need it.
When you read out the strings, just don't store them anywhere:
std::vector<int> values;
std::string discard;
int value;
while (file >> discard >> value) {
values.push_back(value);
}
I guess I must be overdue to post a ctype facet to ignore the strings and read only the data we care about:
#include <locale>
#include <fstream>
#include <vector>
#include <iostream>
#include <algorithm>
#include <iterator>
struct number_only: std::ctype<char> {
number_only() : std::ctype<char>(get_table()) {}
static mask const *get_table() {
static std::vector<mask> rc(table_size, space);
std::fill_n(&rc['0'], 10, digit);
rc['-'] = punct;
return &rc[0];
}
};
int main() {
// open the file
std::ifstream x("config.txt");
// have the file use our ctype facet:
x.imbue(std::locale(std::locale(), new number_only));
// initialize vector from the numbers in the file:
std::vector<int> numbers((std::istream_iterator<int>(x)),
std::istream_iterator<int>());
// display what we read:
std::copy(numbers.begin(), numbers.end(),
std::ostream_iterator<int>(std::cout, "\n"));
return 0;
}
This way the extraneous data is really and truly ignored -- after imbuing the stream with our facet, it's as if the strings don't exist at all.
This method doesn't store the string at all (like was asked for in the question):
static const std::streamsize max = std::numeric_limits<std::streamsize>::max();
std::vector<int> values;
int value;
while(file.ignore(max, ' ') >> file >> value)
{
values.push_back(value);
}
It uses ignore instead of reading the string and not using it.
You can define a structure and then overload the istream operator>> for it:
struct ParameterDiscardingName {
int value;
operator int() const {
return value;
}
};
istream& operator>>(istream& is, ParameterDiscardingName& param) {
std::string discard;
return is >> discard >> param.value;
}
ifstream file("Parameters.cfg");
istream_iterator<ParameterDiscardingName> begin(file), end;
vector<int> parameters(begin, end);
I am trying to read each 'char' of the input file and write in the output file until finds the '?' as the end of file . Every char is written in output file except the spaces between words. I dont know what's wrong in this code??
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream infile("in.txt");
ofstream outfile("out.txt");
char ch;
infile >> ch;
while(ch != '?')
{
outfile<<ch;
infile >> ch;
}
}
Try using noskipws on read...
infile >> noskipws >> ch;
The noskipws tells the input stream to not skip whitespace which it does by default.
istream operator >> ignores whitespace. Try this:
std::string s;
std::getline(infile,s,'?');
outfile << s;
The >> operator for input streams is generally associated with interpretation. For example, when reading strings it skips spaces. This may be the case when reading chars.
You ought to use the read method, for example:
infile.read(&ch, 1)
See http://www.cplusplus.com/reference/iostream/ifstream/ for reference
edit I forgot about the get method. That'll get you a single character, cast as an int. The read method is more geared for reading a chunk of data in one call.
#Andrew White has already pointed out how to fix the problem you've seen. I'll toss in my idea (typically for me, probably over-engineered) idea of how to do the rest of the job:
#pragma once
#if !defined(SENTINEL_ITERATOR_H_)
#define SENTINEL_ITERATOR_H_
#include <istream>
#include <iterator>
template <class T,
class charT=char,
class traits=std::char_traits<charT>,
class distance = ptrdiff_t>
class sentinel_iterator :
public std::iterator<std::input_iterator_tag,distance,void,void,void>
{
std::basic_istream<charT,traits> *is;
T value;
public:
typedef charT char_type;
typedef traits traits_type;
typedef std::basic_istream<charT,traits> istream_type;
sentinel_iterator(istream_type& s)
: is(&s)
{ s >> value; }
sentinel_iterator(T const &s) : is(0), value(s) { }
const T &operator*() const { return value; }
const T *operator->() const { return &value; }
sentinel_iterator &operator++() {
(*is)>>value;
return *this;
}
sentinel_iterator &operator++(int) {
sentinel_iterator tmp = *this;
(*is)>>value;
return (tmp);
}
bool operator==(sentinel_iterator<T,charT,traits,distance> const &x) {
return value == x.value;
}
bool operator!=(sentinel_iterator<T,charT,traits,distance> const &x) {
return !(value == x.value);
}
};
#endif
Then the code becomes something like this:
#include <algorithm>
#include <fstream>
#include "sentinel_iterator.h"
int main() {
ifstream infile("in.txt");
ofstream outfile("out.txt");
infile >> noskipws;
std::copy(sentinel_iterator<char>(infile),
sentinel_iterator<char>('?'),
std::ostream_iterator<char>(outfile));
return 0;
}
I have the following file/line:
pc=1 ct=1 av=112 cv=1100 cp=1700 rec=2 p=10001 g=0 a=0 sz=5 cr=200
pc=1 ct=1 av=113 cv=1110 cp=1800 rec=2 p=10001 g=0 a=10 sz=5 cr=200
and so on.
I wish to parse this and take the key value pairs and put them in a structure:
struct pky
{
pky() :
a_id(0),
sz_id(0),
cr_id(0),
cp_id(0),
cv_id(0),
ct_id(0),
fr(0),
g('U'),
a(0),
pc(0),
p_id(0)
{ }
};
wherein either all the structure fields are used or some might be omitted.
How do I create a C++ class, which will do the same? I am new to C++ and not aware of any functions or library which would do this work.
Each line is to be processed, and the structure will be populated with one line each time and used, before it is flushed. The structure is later used as a parameter to a function.
You can do something like this:
std::string line;
std::map<std::string, std::string> props;
std::ifstream file("foo.txt");
while(std::getline(file, line)) {
std::string token;
std::istringstream tokens(line);
while(tokens >> token) {
std::size_t pos = token.find('=');
if(pos != std::string::npos) {
props[token.substr(0, pos)] = token.substr(pos + 1);
}
}
/* work with those keys/values by doing properties["name"] */
Line l(props["pc"], props["ct"], ...);
/* clear the map for the next line */
props.clear();
}
i hope it's helpful. Line can be like this:
struct Line {
std::string pc, ct;
Line(std::string const& pc, std::string const& ct):pc(pc), ct(ct) {
}
};
now that works only if the delimiter is a space. you can make it work with other delimiters too. change
while(tokens >> token) {
into for example the following, if you want to have a semicolon:
while(std::getline(tokens, token, ';')) {
actually, it looks like you have only integers as values, and whitespace as delimiters. you might want to change
std::string token;
std::istringstream tokens(line);
while(tokens >> token) {
std::size_t pos = token.find('=');
if(pos != std::string::npos) {
props[token.substr(0, pos)] = token.substr(pos + 1);
}
}
into this then:
int value;
std::string key;
std::istringstream tokens(line);
while(tokens >> std::ws && std::getline(tokens, key, '=') &&
tokens >> std::ws >> value) {
props[key] = value;
}
std::ws just eats whitespace. you should change the type of props to
std::map<std::string, int> props;
then too, and make Line accept int instead of std::string's. i hope this is not too much information at once.
This is the perfect place to define the stream operators for your structure:
#include <string>
#include <fstream>
#include <sstream>
#include <istream>
#include <vector>
#include <algorithm>
#include <iterator>
std::istream& operator>> (std::istream& str,pky& value)
{
std::string line;
std::getline(str,line);
std::stringstream dataStr(line);
static const std::streamsize max = std::numeric_limits<std::streamsize>::max();
// Code assumes the ordering is always as follows
// pc=1 ct=1 av=112 cv=1100 cp=1700 rec=2 p=10001 g=0 a=0 sz=5 cr=200
dataStr.ignore(max,'=') >> value.pc;
dataStr.ignore(max,'=') >> value.ct_id;
dataStr.ignore(max,'=') >> value.a; // Guessing av=
dataStr.ignore(max,'=') >> value.cv_id;
dataStr.ignore(max,'=') >> value.cp_id;
dataStr.ignore(max,'=') >> value.fr; // Guessing rec=
dataStr.ignore(max,'=') >> value.p_id;
dataStr.ignore(max,'=') >> value.g;
dataStr.ignore(max,'=') >> value.a_id;
dataStr.ignore(max,'=') >> value.sz_id;
dataStr.ignore(max,'=') >> value.cr_id;
return str;
}
int main()
{
std::ifstream file("plop");
std::vector<pky> v;
pky data;
while(file >> data)
{
// Do Somthing with data
v.push_back(data);
}
// Even use the istream_iterators
std::ifstream file2("plop2");
std::vector<pky> v2;
std::copy(std::istream_iterator<pky>(file2),
std::istream_iterator<pky>(),
std::back_inserter(v2)
);
}
This seemed to do the trick. Of course you'd extract the code I've written in main and stick it in a class or something, but you get the idea.
#include <sstream>
#include <string>
#include <vector>
#include <map>
using namespace std;
vector<string> Tokenize(const string &str, const string &delim)
{
vector<string> tokens;
size_t p0 = 0, p1 = string::npos;
while(p0 != string::npos)
{
p1 = str.find_first_of(delim, p0);
if(p1 != p0)
{
string token = str.substr(p0, p1 - p0);
tokens.push_back(token);
}
p0 = str.find_first_not_of(delim, p1);
}
return tokens;
}
int main()
{
string data = "pc=1 ct=1 av=112 cv=1100 cp=1700 rec=2 p=10001 g=0 a=0 sz=5 cr=200 pc=1 ct=1 av=113 cv=1110 cp=1800 rec=2 p=10001 g=0 a=10 sz=5 cr=200";
vector<string> entries = Tokenize(data, " ");
map<string, int> items;
for (size_t i = 0; i < entries.size(); ++i)
{
string item = entries[i];
size_t pos = item.find_first_of('=');
if(pos == string::npos)
continue;
string key = item.substr(0, pos);
int value;
stringstream stream(item.substr(pos + 1));
stream >> value;
items.insert (pair<string, int>(key, value));
}
}
Unfortunately, your source data file is human-oriented, which means that you're going to have to do a bunch of string parsing in order to get it into the structure. Otherwise, if the data had been written directly as a binary file, you could just use fread() to pop it directly into the struct.
If you want to use an "elegant" (ie, ugly minimalistic approach), you could make a loop of sorts to parse each line, basically using strchr() to first find the '=' character, then the next space, then using atoi() to convert each number into a real int, and then using some pointer hackery to push them all into the structure. The obvious disadvantage there is that if the structure changes, or is even reorganized somehow, then the whole algorithm here would silently break.
So, for something that would be more maintainable and readable (but result in more code), you could just push each value into a vector, and then go through the vector and copy each value into the appropriate strucutre field.
What you get taught here, are monstrosities.
http://en.wikipedia.org/wiki/Scanf
Do not use this function to extract strings from untrusted data, but as long as you either trust data, or only get numbers, why not.
If you are familiar with Regular Expressions from using another language, use std::tr1::regex or boost::regex - they are the same. If not familiar, you will do yourself a favor by familiarizing yourself.