For this program i have only used field separators from data files in shell script. But I am trying to use the standard library function ifstream() to read in from a data file. The only problem is I am getting the data like so
A:KT5:14:executive desk:
This is for a hash table, and I need to separate the values in the line for the data structure as well as the transaction type. I’ve been looking around the web and have not found much on field separators and what I have found was quite confusing.
The question then being, is there a way to set a field separator with the ifstream function or is there another standard library i/o function I should be using?
Thanks.
ifstream. This data type represents the input file stream and is used to read information from files. 3. fstream. This data type represents the file stream generally, and has the capabilities of both ofstream and ifstream which means it can create files, write information to files, and read information from files.
std::ifstream Input stream class to operate on files. Objects of this class maintain a filebuf object as their internal stream buffer, which performs input/output operations on the file they are associated with (if any). File streams are associated with files either on construction, or by calling member open .
No, this is done automatically by the ifstream destructor. The only reason you should call it manually, is because the fstream instance has a big scope, for example if it is a member variable of a long living class instance.
@Steve Townsend has already pointed out one possibility. If you prefer to use operator>>
instead of std::getline
, you can do that as well. An istream
always treats whitespace as a separator. Each stream has an associated locale, and each locale includes a ctype
facet. That ctype
facet is what the istream
uses to determine what input characters are whitespace.
In your case, you apparently want the stream to treat only newlines and colons as "whitespace" (i.e., separators), while the actual space character is just treated as a "normal" character, not a separator.
To do that, you can create a ctype facet like this:
struct field_reader: std::ctype<char> {
field_reader(): std::ctype<char>(get_table()) {}
static std::ctype_base::mask const* get_table() {
static std::vector<std::ctype_base::mask>
rc(table_size, std::ctype_base::mask());
rc['\n'] = std::ctype_base::space;
rc[':'] = std::ctype_base::space;
return &rc[0];
}
};
To use, this, you have to "imbue" the stream with a locale using this facet:
int main() {
std::stringstream input("A:KT5:14:executive desk:");
// have the stream use our ctype facet:
input.imbue(std::locale(std::locale(), new field_reader()));
// copy fields from the stream to standard output, one per line:
std::copy(std::istream_iterator<std::string>(input),
std::istream_iterator<std::string>(),
std::ostream_iterator<std::string>(std::cout, "\n"));
return 0;
}
I'm the first to admit, however, that this has some shortcomings. First of all, locales and facets are generally pretty poorly documented, so most C++ programmers are likely to find this fairly difficult to understand (especially when all the real work happens "under the covers", so to speak).
Another possibility is to use Boost Tokenizer. In all honesty, this is a little more work to use -- it'll require that you do something like reading a string, then breaking it up separately. At the same time, it's well documented, pretty widely known, and fits enough better with people's preconceptions about how to do things like that, that quite a few people will probably find it easier to follow despite the extra complexity.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With