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.
Related
ifstream file ("../file.csv");
string test;
unsigned counter = 0;
while(counter<10){
getline(file,test,'"');
cout<<test<<endl;
counter++;
}
I am essentially trying to recreate this in c++ but without using the string class. I am trying to figure out a way to not use the string class and still use this same function. Any thoughts?
For some clarification I will be delimeting with a '"' and then I will be a comma after that, so there is a text surrounded in '"', and a conclusion that is seperated from the text by a comma.
This is my custom class
class customString {
char* myStr;
int length;
public:
customString();
customString(char enb);
customString(const customString& source);
customString(customString&& source);
~customString(){delete myStr;}
};
customString::customString(){
str = new char[1];
str[0] = '\0';
}
customString::customString(char enb) {
length = 1;
myStr= new char(enb);
}
customString::customString(const customString &source) {
length = source.length;
myStr = new char[length];
for (int i = 0; i < length; i++){
myStr[i]=source.myStr[i];
}
}
customString::customString(const char* val){
if(val!= nullptr){
int counter = 0;
while(val[counter]!='\0')counter++; //Find length of the char array
length = counter;
myStr = new char[counter];
for(int i =0;i<counter;i++){
myStr[i]=val[i];
}
}else{
length = 1;
myStr = new char[length];
myStr[0] = '\0';
}
}
customString::~customString(){
delete[] myStr;
}```
you can use std::basic_istream<CharT,Traits>::getline:
basic_istream& getline( char_type* s, std::streamsize count, char_type delim );
There isn't much to your custom class, but as it is implemented, you need to define an assignment operator, otherwise an assignment could result in both a memory leak and a double free bug. Follow the Rule of Three.
If you must use a pointer, you should at least use a std::unique_ptr<char[]> instead of a bare pointer. However, it is simpler to use a std::vector<char> instead.
class customString {
std::vector<char> myStr;
friend std::ostream & operator << (std::ostream &ost, const customString &s) {
return ost.write(&s.myStr[0], s.myStr.size());
}
friend std::istream & getline (std::istream &ist, customString &s,
char delim = '\n') {
//...
return ist;
}
public:
customString() {}
customString(char enb) : myStr(enb) {}
customString(const char *val, int len = -1)
: myStr(val, val + (len < 0 ? strlen(val) : len))
{}
};
Note that your code was missing the const char * constructor. Also, since std::vector knows its length, the length member is no longer required. For convenience, I included a method to emit the contents into an std::ostream.
For your getline replacement, you could also implement that as a friend function inside your custom class, and I included a stub above. When you implement it, you have to pay attention to how std::getline is defined to make sure you follow all of its behaviors.
My advice is to actually extract one character at a time (e.g., with std::istream::get) and check it against the delimiter. Then, make sure you set failbit in the std::istream if you encounter one of the failure conditions.
str.max_size() characters have been stored
If no characters were extracted for whatever reason
Reference: cppreference.com
Good luck, and hope this helps!
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 have been trying to read data from a binary file in C++, but I'm getting run time error, Program has stopped working!
I have used the similar code before and it is still working. I am getting an error while executing the constructor of the class SettingsClass [Maybe because of the read function, because after removing it, everything just ran great.]
struct Setting{
int SettingID;
int SettingINTValue;
double SettingDOUBLEValue;
char SettingCHARValue;
string SettingSTRINGValue;
string SettingName;
};
class SettingsClass {
public:
void ResetSettings() {
fstream SettingFile;
Setting defaultsetting[NoOfSettings];
for(int i=1;i<=NoOfSettings;i++) {
defaultsetting[i-1].SettingID = i;
defaultsetting[i-1].SettingINTValue = 0;
defaultsetting[i-1].SettingDOUBLEValue = 0.0;
defaultsetting[i-1].SettingCHARValue = '#';
defaultsetting[i-1].SettingSTRINGValue = "null";
switch(i) {
default:
defaultsetting[i-1].SettingName = "Compression Levels";
defaultsetting[i-1].SettingSTRINGValue = "Normal";
defaultsetting[i-1].SettingINTValue = 1;
break;
}
cout<<i<<". "<<defaultsetting[i-1].SettingName<<"\n\t "<<defaultsetting[i-1].SettingINTValue<<"\n\t "<<defaultsetting[i-1].SettingDOUBLEValue<<"\n\t "<<defaultsetting[i-1].SettingCHARValue<<"\n\t "<<defaultsetting[i-1].SettingSTRINGValue<<"\n\t ";
cout<<"\n";
}
SettingFile.open(SettingsFilePath,ios::binary|ios::out);
if(SettingFile.is_open()){
SettingFile.write(reinterpret_cast<char const *>(&defaultsetting),sizeof(defaultsetting));
} else {
cout<<"Error!";
}
SettingFile.close();
}
SettingsClass() {
fstream SettingFile;
SettingFile.open(SettingsFilePath,ios::binary|ios::in);
if(SettingFile.is_open()) {
Setting TempSettings[NoOfSettings];
SettingFile.read((char*)&TempSettings,sizeof(TempSettings));
} else {
cout<<"Error...";
}
SettingFile.close();
}
} Settings;
You should go read and learn more about file streams and the associated input and output operators << and >>. You cannot simply input characters into an array like this line of code:
SettingFile.read((char*)&TempSettings,sizeof(TempSettings));
The array is not of char and yet you cast it as such. Instead you should loop over the available input and fill in the array, e.g.:
for(size_t i = 0; i<NoSetting; ++i) {
SettingFile >> TempSettings[i];
}
Of course you should overload the appropriate input operator:
istream& operator>>(istream& _is, Setting& _s) {
//read all variables of setting, i.e.:
//_is >> _s.var1;
//_is >> _s.var2;
//etc.
}
You likely have the same error for your output. You should overload:
ostream& operator<<(ostream& _os, const Setting& _s) {
//output all variables of Setting, e.g.:
//_os << _s.var1;
}
Do something like this instead of filestream write:
for(size_t i = 0; i<NoSetting; ++i) {
SettingsFile << defaultSetting[i];
}
This would be great to avoid copying. Is there anything in std or boost to do this?
std::string mystring = "HELLO "; //initial string
int len = mystring.size(); //get initial length
mystring.resize(100); //resize so it's big enough
char* buffer = &mystring[len-1]+1; //get pointer to "spare" space
std::cin.get(buffer , 100-len, '\n'); //read into buffer, until newline
mystring.resize(len + std::cin.gcount()); //shrink to correct size
Since there were no existing solutions, this is what I came up with:
istream& appendline(istream& is, string& str, char delim = '\n')
{
size_t size = str.size();
size_t capacity = str.capacity();
streamsize spaceRemaining = capacity - size;
if (spaceRemaining == 0)
{
capacity = max(static_cast<size_t>(8), capacity * 2);
spaceRemaining = capacity - size;
}
// give getline access to all of capacity
str.resize(capacity);
// get until delim or spaceRemaining is filled
is.getline(&str[size], spaceRemaining, delim);
// gcount includes the delimiter but not the null terminator
size_t newSize = size + is.gcount();
// is failbit set?
if (!is)
{
// if string ran out of space, expand and retry
if (is.gcount()+1 == spaceRemaining)
{
is.clear();
str.resize(newSize);
str.reserve(capacity * 2);
return appendline(is, str, delim);
}
}
else if (!is.eof())
--newSize;
// resize string to fit its contents
str.resize(newSize);
return is;
}
You can use this member function instead (doc):
istream& getline (char* s, streamsize n, char delim );
to read data, and then append that data to a string.
For example, you can wrap this functionality in your own defined function as:
std::istream & getline(std::istream &in, std::string & str, char delim)
{
char buf[1024];
in.getline(buf, 1024, delim);
str.append(buf, in.gcount());
return in;
}
std::string s = "initial string";
getline(std::cin, s, '\n'); //should append to s
Just use the global std::getline instead of the member method
stringstream s;
s << "line1\nline2";
string str;
while(std::getline(s, str)) cout << str;
output: line1line2
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;
}