I have a file which looks like:
123,Cheese,Butter
78,Milk,Vegetable,Fish
and I wish to read each line into a data type List which has int num and char things[3][10] using overloaded operator >>. So far I have:
friend istream& operator>> (istream &is, List &rhs)
{
char comma;
is >> rhs.num >> comma >> ... (I don't know how to continue)
return is;
} // operator>>
Am I doing it right using char comma to skip a comma? How do I read different entries with different lengths separated by comma without using string?
It will be only a pseudocode but if you really need to avoid std::string your best choice is to make it more or less look like this:
istream &operator >>(istream &s, YourType &mylist) {
char mybuf[256];
s.read(mybuf, 256);
char *beg = mybuf;
char *cur = beg;
while (cur != mybuf + 256 && *cur!=0) {
if (*cur == '\n') {
mylist.addnext();
}
if (*cur == ',') {
*cur = 0; //to make the char string end on each comma
mylist.current.add(beg);
beg = cur + 1;
}
}
}
Remember that if YourType will be for example vector<vector<const char *>> you will need to add the operator >> into the std namespace.
Related
These are the data in my Login.csv file:
ID,Name,Password,Gender
1,Liam,1234,M
2,Janice,0000,F
So probably I'll use class & objects to create login details, and write it into the file. After that I will split the csv from file into a vector of strings, from there how do I load back the details to objects of class.
This is my code of splitting the csv from file:
int _tmain(int argc, _TCHAR* argv[])
{
string line;
ifstream fin("users.csv");
while (getline(fin, line)){
vector<string> token;
split(line, ',', token);
for (int i = 0; i < token.size(); i++){
cout << token[i] << " ";
//////////// <<here>>
}
cout << endl;
}
system("pause");
return 0;
}
void split(const string& s, char c, vector<string>& v) {
string::size_type i = 0;
string::size_type j = s.find(c);
while (j != string::npos) {
v.push_back(s.substr(i, j - i));
i = ++j;
j = s.find(c, j);
if (j == string::npos)
v.push_back(s.substr(i, s.length()));
}
}
I was thinking how can I set the splitted strings from the string vector to a vector of objects, something like this: (to put in the << here >> section i commented in above)
vector<Login>loginVector;
//all the objects below should set from string vector (token)
loginVector[i].setID(); //i=0, id=1, name=Liam, password=1234, gender=M
loginVector[i].setName();
loginVector[i].setPassword();
loginVector[i].setGender();
loginVector[i].setID(); //i=1, id=2, name=Janice, password=0000, gender=M
loginVector[i].setName();
loginVector[i].setPassword();
loginVector[i].setGender();
Thank you.
Implement your Login object and populate it in the loop.
struct Login {
enum Gender {
Male,
Female
};
int Id;
std::string Name;
std::string Password;
/* you should probably use a constructor */
};
/* to construct your vector */
int I = 0;
while(I < token.size()) {
/* in your iterator */
Login& LO = loginVector.emplace_back(Login{});
LO.Id = std::stoi(token[++I]);
LO.Name = token[++I];
/* etc...*/
}
Note that this assumes your CSV is well formed, up to you to implement all the checking and make sure you handle corner cases like possible errors from stoi, blank rows or missing columns.
Also, don't do system("pause");, you're executing a shell to sleep for you, which has massive overhead compared to just using sleep which does literally the same thing except in a far more direct way.
I personally would implement this by adding an extraction operator for your class.
You'll have to friend the extraction operator because it must be defined externally to your class, since it's actually operating on the istream and not on your class, so something like this should be defined in your class:
friend istream& operator>> (istream& lhs, Login& rhs);
Depending on how your variables are named your extraction operator should look something like this:
istream& operator>> (istream& lhs, Login& rhs) {
char gender;
lhs >> rhs.id;
lhs.ignore(numeric_limits<streamsize>::max(), ',');
getline(lhs, rhs.name, ',');
getline(lhs, rhs.password, ',');
lhs >> ws;
lhs.get(gender);
rhs.isMale = gender == 'm' || gender == 'M';
return lhs;
}
Live Example
I am trying to extract a string from an istream with strings as delimiters, yet i haven't found any string operations with behavior close to such as find() or substr() in istreams.
Here is an example istream content:
delim_oneFUUBARdelim_two
and my goal is to get FUUBAR into a string with as little workarounds as possible.
My current solution was to copy all istream content into a string using this solution for it and then extracting using string operations. Is there a way to avoid this unnecessary copying and only read as much from the istream as needed to preserve all content after the delimited string in case there are more to be found in similar fashion?
You can easily create a type that will consume the expected separator or delimiter:
struct Text
{
std::string t_;
};
std::istream& operator>>(std::istream& is, Text& t)
{
is >> std::skipws;
for (char c: t.t_)
{
if (is.peek() != c)
{
is.setstate(std::ios::failbit);
break;
}
is.get(); // throw away known-matching char
}
return is;
}
See it in action on ideone
This suffices when the previous stream extraction naturally stops without consuming the delimiter (e.g. an int extraction followed by a delimiter that doesn't start with a digit), which will typically be the case unless the previous extraction is of a std::string. Single-character delimiters can be specified to getline, but say your delimiter is "</block>" and the stream contains "<black>metalic</black></block>42" - you'd want something to extract "<black>metallic</black>" into a string, throw away the "</block>" delimiter, and leave the "42" on the stream:
struct Until_Delim {
Until_Delim(std::string& s, std::string delim) : s_(s), delim_(delim) { }
std::string& s_;
std::string delim_;
};
std::istream& operator>>(std::istream& is, const Until_Delim& ud)
{
std::istream::sentry sentry(is);
size_t in_delim = 0;
for (char c = is.get(); is; c = is.get())
{
if (c == ud.delim_[in_delim])
{
if (++in_delim == ud.delim_.size())
break;
continue;
}
if (in_delim) // was part-way into delimiter match...
{
ud.s_.append(ud.delim_, 0, in_delim);
in_delim = 0;
}
ud.s_ += c;
}
// may need to trim trailing whitespace...
if (is.flags() & std::ios_base::skipws)
while (!ud.s_.empty() && std::isspace(ud.s_.back()))
ud.s_.pop_back();
return is;
}
This can then be used as in:
string a_string;
if (some_stream >> Until_Delim(a_string, "</block>") >> whatevers_after)
...
This notation might seem a bit hackish, but there's precedent in Standard Library's std::quoted().
You can see the code running here.
Standard streams are equipped with locales that can do classification, namely the std::ctype<> facet. We can use this facet to ignore() characters in a stream while a certain classification is not present in the next available character. Here's a working example:
#include <iostream>
#include <sstream>
using mask = std::ctype_base::mask;
template<mask m>
void scan_classification(std::istream& is)
{
auto& ctype = std::use_facet<std::ctype<char>>(is.getloc());
while (is.peek() != std::char_traits<char>::eof() && !ctype.is(m, is.peek()))
is.ignore();
}
int main()
{
std::istringstream iss("some_string_delimiter3.1415another_string");
double d;
scan_classification<std::ctype_base::digit>(iss);
if (iss >> d)
std::cout << std::to_string(d); // "3.1415"
}
I have a text file in form:
a 1 b 6 e 4 h 2
r 5 q 1 a 2 e 2 b 3
up to 300k line. every char follows with its value. I want to read this file line by line, and do pair between each 2 chars in the same line. the results of first line:
(a,b)
(a,e)
(a,h)
(b,e)
(b,h)
(e,h)
I also, count the frequency for each pair. The code for that:
using namespace std;
std::map<std::pair< char, int>, int> CharP;
struct Mydata
{
char myString;
float value;
friend std::istream &operator>>(std::istream &is, Mydata &d) {
{
string line;
std::getline(is,line);
return is >> d.myString>> d.value;
}
};
int main()
{
std::ifstream in("Data.txt");
std::vector<Mydata> data{ std::istream_iterator<Mydata>(in), std::istream_iterator<Mydata>() };
for (std::vector<Mydata> ::iterator current = data.begin(); current != data.end(); ++current)
{
char a = current->myString;
for (std::vector<Mydata> ::iterator current2 = ++current; current2 != data.end(); ++current2)
{
char b = current2->myString;
auto itt = CharP.find(std::make_pair(b, a));
++CharP[(itt != CharP.end()) ? std::make_pair(b, a) : std::make_pair(a, b)];
}
--current;
}
cout << " \nPress any key to continue\n";
system("PAUSE");
return 0;
}
I got this error:
binary '>>' : no operator found which takes a right-hand operand of type 'Mydata' (or there is no acceptable conversion).
I have another concern, since I have large file, I'm wondering if there is another method faster than the attached code.
Without having looked at the rest of the code I think I can safely say this is the problem:
friend std::istream& getline(std::istream &is, Mydata &d)
Were you intending on overloading the extractor in this declaration? If so, you should know that the extractor is an operator, namely operator>>(). Replace the getline with >>:
friend std::istream& operator>>(std::istream &is, Mydata &d);
Also, the following is very unreadable:
++CharP[(itt != CharP.end()) ? std::make_pair(b, a) : std::make_pair(a, b)];
replace it with this:
if (itt != CharP.end())
{
auto p = std::make_pair(b, a);
++CharP[p];
} else
{
auto p = std::make_pair(a, b);
++CharP[p];
}
Question
The problem is that i am trying to get user input using insertion operator and initialising the value thechars, to allocate the size to thechars i need the length of input, how do i get it?? And initialise in insertion operator.
Main problem is with insertion operator.
When i run the program it shows the segmentation fault,
plz help
class string1
{
private:
int len;
char *thechars;
//friend ostream& operator<<(ostream&,string1&);##
//friend istream& operator>>(istream&,string1&);##
public:
//string1() :len(0),thechars(NULL){}
string1()
{
thechars = new char[1];
thechars[0] = '\0';
len=0;
// cout << "\tDefault string constructor\n";
// ConstructorCount++;
}
};
// this is the insertion operator i use
istream& operator>>(istream& in, string1& tpr)
{
in >> tpr.thechars;
//tpr.thechars[i+1]='\0';
return in;
}
//this one is the extraction operator
ostream& operator<<(ostream& out,string1& prt)
{
for(int i=0;i<prt.len;i++)
out<<prt.thechars[i];
return out;
}
// main function##
string1 str;
cout << "enter first string" << endl;
cin >> str;
cout << str << endl;
If in is a file input stream, you can do the following:
in.seekg(0, ios::end);
length = in.tellg();
in.seekg(0, ios::beg);
The other option is reading the input stream char by char and double the size of thechars each time it's exhausted. First, introduce one more variable to store the currently allocated size of the buffer --- allocSize. After that update the constructor and operator<< as follows.
Constructor:
string1()
{
allocSize = 1; // initially allocated size
thechars = new char[allocSize];
thechars[0] = '\0';
len=0;
}
Input operator:
istream& operator>>(istream& in, string1& tpr)
{
char inp;
while (in.get(inp)) {
// end-of-input delimiter (change according to your needs)
if (inp == ' ')
break;
// if buffer is exhausted, reallocate it twice as large
if (tpr.len == tpr.allocSize - 1) {
tpr.allocSize *= 2;
char *newchars = new char[tpr.allocSize];
strcpy(newchars, tpr.thechars);
delete[] tpr.thechars;
tpr.thechars = newchars;
}
// store input char
tpr.thechars[tpr.len++] = inp;
tpr.thechars[tpr.len] = '\0';
}
}
But the best option is to use std::string as a type for thechars. Do you really need all this manual memory handling?
Instead of giving the in a char* give it a regular string. Then you can extract the data yourself.
istream& operator>>(istream& in, string1& tpr)
{
string temp;
in >> temp;
tpr.len = temp.length + 1;
tpr.thechars = new char[tpr.len];
tpr.thechars[temp.length] = '\0';
strcpy(tpr.thechars, &temp[0], tpr.len);
return in;
}
you wrote
in>> tpr.thechars; // where thechars="\0";
You allocated only one byte, but i guess you are input string with more bytes.
I think error here.
I just have a quick question. I need to override the operator >> for a custom String class and I can't quite figure out how to do it.
I know that this code works, because it was my original method of solving the problem:
istream& operator>>(istream &is, String &s) {
char data[ String::BUFF_INC ]; //BUFF_INC is predefined
is >> data;
delete &s;
s = data;
return s;
}
However, according to the spec (this is a homework assignment), I need to read in the characters 1 at a time to manually check for whitespace and ensure that the string isn't too big for data[]. So I changed my code to the following:
istream& operator>>(istream &is, String &s) {
char data[ String::BUFF_INC ];
int idx = 0;
data[ 0 ] = is.get();
while( (data[ idx ] != *String::WHITESPACE) && !is.ios::fail() ) {
++idx;
is.get();
data[ idx ] = s[ idx ];
}
return is;
}
When this new code is executed however it just gets stuck in a loop of user input. So how do I use is.get() to read in the data character by character but not wait for more user input? Or should I perhaps be using something other than .get()?
You don't seem to be doing anything with the character you get from the stream
istream& operator>>(istream &is, String &s) {
char data[ String::BUFF_INC ];
int idx = 0;
data[ 0 ] = is.get();
while( (data[ idx ] != *String::WHITESPACE) && !is.ios::fail() ) {
++idx;
is.get(); // you don't do anything with this
data[ idx ] = s[ idx ]; // you're copying the string into the buffer
}
return is;
}
So it checks whether the string s contains a whitespace, not whether you read a whitespace from the stream.
Try:
istream& operator>>(istream &is, String &s)
{
std::string buffer;
is >> buffer; // This reads 1 white space separated word.
s.data = buffer.c_str();
return is;
}
Commenting on your original code:
istream& operator>>(istream &is, String &s)
{
char data[ String::BUFF_INC ];
is >> data; // Will work. But prone to buffer overflow.
delete s; // This line is definately wrong.
// s is not a pointer so I don;t know what deleting it would do.
s = data; // Assume assignment operator is defined.
// for your class that accepts a C-String
return s;
}
Using the second version as a base:
istream& operator>>(istream &is, String &s)
{
std::vector<char> data;
char first;
// Must ignore all the white space before the word
for(first = is.get(); String::isWhiteSpace(first) && is; first = is.get())
{}
// If we fond a non space first character
if (is && !String::isWhiteSpace(first))
{
data.push_back(first);
}
// Now get values while white space is false
char next;
while( !String::isWhiteSpace(next = is.get()) && is)
{
// Note we test the condition of the stream in the loop
// This is because is.get() may fail (with eof() or bad()
// So we test it after each get.
//
// Normally you would use >> operator but that ignores spaces.
data.push_back(next);
}
// Now assign it to your String object
data.push_back('\0');
s.data = data;
return is;
}