Getting garbage when using getline() - c++

I am practicing classes and I am trying to allow the user to enter its name using a space. When the user enters a space, my output is "garbage". Is it possible to use the get.line function with a class? This is what I have.
//Practicing classes.
#include <iostream>
#include <string>
using namespace std;
//Class decleration
class person
{
//Access Specifier
public:
string name; //Member Vairable Decleration
int number; //Member Function Decleration
};
//Main function.
int main()
{
//Object creation from class.
person obj;
//Get input Values for object Variables
cout<<"Enter the Name: \n";
cin>>obj.name;
cin.getline(obj.name);
cout<<"Enter the number:\n";
cin>>obj.number;
//Show the output
cout<< obj.name <<": " << obj.number << endl;
return 0;
}

Change this:
cin>>obj.name;
cin.getline(obj.name);
to:
std::getline (std::cin, obj.name);
as specified by the manual. You can omit std::, since you are already using namespace std.
Example run:
Enter the Name:
samaras
Enter the number:
1
samaras: 1
However notice that the data members of a class are usually on private scope and public functions can apply to them. Also, as Thomas Matthews put it: "This breaks data hiding and encapsulation rules. I highly recommend that the functionality for reading the data member be placed in the class.".
You may want to look into overloading operator >>. However, if you feel like you are not done with understanding the classes, I would suggest to leave that for later.

You said that you want to return the full name of the user, So I create a simple function that returns a concatenated value using the first last name. but if you want you should go over tuple or pair to make advance function.
#include <iostream>
#include <string>
using namespace std;
class person
{
//Access Specifier
public:
string firstname;
string lastname; //Member Vairable Decleration
int number; //Member Function Decleration
};
string returnCompleteName(string firstname, string lastname)
{
return firstname + " " + lastname;
}
//Main function.
int main()
{
//Object creation from class.
person obj;
//Get input Values for object Variables
cout<<"Enter the FirstName: \n";
cin>>obj.firstname;
cout<<"Enter the LastName: \n";
cin>>obj.lastname;
cout<<"Enter`enter code here` the number:\n";
cin>>obj.number;
//Show the output
cout<< returnCompleteName(obj.firstname,obj.lastname) <<": " << obj.number << endl;
return 0;
}

I also had the same problem using the specified answers, because I didn't realize my file was encoded in UCS-2. This helped : How to read a UCS-2 file?
Wide streams use a wide stream buffer to access the file. The Wide
stream buffer reads bytes from the file and uses its codecvt facet to
convert these bytes to wide characters. The default codecvt facet is
std::codecvt which converts between the
native character sets for wchar_t and char (i.e., like mbstowcs()
does).
You're not using the native char character set, so what you want is a
codecvt facet that reads UCS-2 as a multibyte sequence and converts it
to wide characters.
#include <fstream>
#include <string>
#include <codecvt>
#include <iostream>
int main(int argc, char *argv[])
{
wifstream fin("en.rc", std::ios::binary); // You need to open the file in binary mode
// Imbue the file stream with a codecvt facet that uses UTF-16 as the external multibyte encoding
fin.imbue(std::locale(fin.getloc(),
new std::codecvt_utf16<wchar_t, 0xffff, consume_header>));
// ^ We set 0xFFFF as the maxcode because that's the largest that will fit in a single wchar_t
// We use consume_header to detect and use the UTF-16 'BOM'
// The following is not really the correct way to write Unicode output, but it's easy
std::wstring sLine;
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> convert;
while (getline(fin, sLine))
{
std::cout << convert.to_bytes(sLine) << '\n';
}
}

Open the file you are reading with an hex editor and check if there are some bytes before your visible data. I ended up by removing these bytes overwriting them with 20 hex data which means space, and then cleaning the file with the editor.

Related

How to pass string to gets_s() in C++?

#include<iostream>
using namespace std;
int main() {
string str;
gets_s(str);
cout << str << endl;
return 0;
}
When I tried to run the above code it threw an error that no instance of gets_s() matched the argument list.
How can I pass an std::string instead of a char[] to gets_s() function if is possible?
The C function get_s takes a char* and a length argument, not a std::string.
Your best options are:
Formatted input:
std::cin >> str;
Read a line:
std::getline(std::cin, str);
Don't do that. Use the stream in a normal way:
#include<iostream>
using namespace std;
int main()
{
string str;
cin >> str;
cout << str << endl;
return 0;
}
gets_s has a significant limitation in that you must provide an upper limit on the number of characters you want to read.
Since you are using string the superior alternative is to use getline
#include <iostream>
#include <string>
using namespace std;
string str;
getline(cin, str);
This will expand the string to hold as many characters as are entered by the user.
gets_s() takes two arguments: pointer to char array and maximal size (your call is missing it). You cannot pass std::string - only C style strings.
Instead of C functions, why not use C++ way std::cin >> str or getline(std::cin, str)?
In C also don't use gets_s() (it's optional in C11) or gets() - use fgets() instead.
Well, there are a lot of answers about std::getline, but in case if you really need to use get_s, you may write such code:
size_t length = 10; // Just for example
std::string my_string(length, 0);
get_s(&my_string[0], length); // C++14 and older
get_s(my_string.data(), length); // C++17 and newer

How to use Input String Stream with symbols seperating strings instead of spaces?

note: I just learned about Getline and Streams.
Instead of a space separating first name, last name, and age, how could I separate them with ^ or --?
Is there a function for this? Is this a stupid question, and if so, why?
-The reason for this question is because I was going to make a function for solving polynomial derivatives and failed miserably.
int main() {
istringstream inSS; // Input string stream
string lineString; // Holds line of text
string firstName;
string lastName;
int userAge;
getline(cin, lineString);
inSS.str(lineString);
inSS >> firstName;
inSS >> lastName;
inSS >> userAge;
return 0;
}
The free function getline also offers to have a custom delimiter like user4581301 said. However, it will only extract strings and you also have int.
A similar solution can be found in this answer and I have modified the code to fit your needs: changing the delimiter for cin (c++)
You can use imbue to have some custom delimiter. A simple example is below:
#include <locale>
#include <iostream>
template<char Delim>
struct alternativeDelimiter : std::ctype<char> {
alternativeDelimiter() : std::ctype<char>(get_table()) {}
static mask const* get_table()
{
static mask rc[table_size];
rc[Delim] = std::ctype_base::space;
return &rc[0];
}
};
int main() {
using std::string;
using std::cin;
using std::locale;
cin.imbue(locale(cin.getloc(), new alternativeDelimiter<'^'>));
string word;
while(cin >> word) {
std::cout << word << "\n";
}
}
imbue does take ownership of the ctype, so no worries about calling delete yourself.
If you input some^text, the output will be
some
text
You can also use it with your example, of course.
If you extend the table by writing lines similar to line 11 (rc[Delim] = std::ctype_base::space) only changing Delim, you can have multiple characters that will be interpreted as space.
I am not sure in how far this solves your original problem of writing a math parser, though. The general terminology involves the concepts "parser" and "lexer" and you might research these concepts to build a reliable math solver. Hope it helps, too.

Why is my Library Management program not writing to file

I am creating a library Management which you can add books, dvds and students to. The books and dvds can then be issued to the Students. The books, dvds and students are written to a .dat file.
The problem I am having is that the program doesn't seem to be writing the students to the .dat file and therefore when I search for a student or try to issue a book student it doesn't find the student, it gives me an error saying student not found. Can someone tell me what I am doing wrong.
I have included some of the code below.
Main.cpp
#include <iostream>
#include "Library.h"
int main() {
Library lib1;
while(1)
{
char studentOption;
char name[30];
std::cout<<"1 to add a new student\n";
std::cout<<"2 to search for a student\n";
std::cin.getline( name, 80);
studentOption = name[0];
switch(studentOption){
case '1':
lib1.insertStudent();
break;
case '2':
char regno[6];
std::cout<<"Enter the registration no. of the student you want to search: \n";
std::cin>>regno;
lib1.searchStudent(regno);
break;
}
};
return 0;
};
Library.h
#ifndef _Library_H_
#define _Library_H_
#include <stdio.h>
class Library
{
public:
void insertStudent();
void searchStudent(char regno[]);
};
#endif
Library.cpp
#include <iostream>
#include <fstream>
#include "Library.h"
#include "Student.h"
Student student1;
void Library::insertStudent()
{
std::fstream file;
file.open("student.dat",std::ios::out|std::ios::app);
student1.newStudent();
file.write((char*)&student1,sizeof(Student));
file.close();
}
void Library::searchStudent(char regno[])
{
std::fstream file;
int flag=0;
file.open("student.dat",std::ios::in);
while(file.read((char*)&student1,sizeof(Student)))
{
if((strcmp(student1.retregistrationNo(),regno)==0))
{
student1.displayStudent();
flag=1;
}
}
file.close();
if(flag==0)
std::cout<<"Error: Student not found in the system. \n";
}
Student.cpp
#include "Student.h"
void Student::newStudent()
{
std::cout<<"Enter the registration no. \n";
std::cin>>registrationno;
std::cin.ignore();
std::cout<<"Enter the name of the student \n";
fgets(name, sizeof(name), stdin);
stbookbar[0]='/0';
std::cout<<"Student added to system.\n";
}
void Student::displayStudent()
{
std::cout<<"Enter the registration no. : \n";
std::cin>>registrationno;
std::cin.ignore();
std::cout<<"Enter the name of the student: \n";
puts(name);
}
Student.h
#ifndef STUDENT_H_
#define STUDENT_H_
#include <iostream>
#include <fstream>
class Student
{
char registrationno[6];
char name[20];
char stbookbar[6];
char stdvdbar[6];
public:
void newStudent();
void displayStudent();
char* retregistrationNo()
{
return registrationno;
}
};
#endif
Although the following my not be the exact cause of your issue, they may lead to issues.
Use string not char*
There are soo many ways that a program can fail by using C-Style (char *) strings. There is also the issue of memory management.
Use std::string instead. If you need fixed record lengths, this can be handled at the point when you write the data. All other times you should be using std::string.
Also, you can use operator == with std::string instead of strcmp. The strcmp function will invoke undefined behavior if the two strings are not nul terminated.
Avoid fgets
There are buffer overflow and other issues with fgets. Use std::getline and std::string instead. This combination will handle expanding a string as required. It's safer.
Don't cross the streams
Be consistent, use C++ streams or C-style I/O, don't mix the two. For example, you use cout and in the same function, puts. There is a possibility that the two can use different I/O buffers. I don't see why you don't use cout << name;.
Use Boolean types, not 1 & 0
The C++ language has a type to represent true and false values. The type is bool, use it. The 1 and 0 representation of Boolean values is sooo old fashioned (like circa 1966). Move on to more readable code.
With integers, is the value 3 true or false? What about -1? From reading the code, there are a lot more reasons why an integer is set to 0 than setting a Boolean variable to false.
Readable code prevents many defects.
Use block statements, always.
All if, for, while, do-while should use blocks
Change your coding style to use '{' and '}' for single line and multi-line statements.
Avoid Magic Numbers
A magic number is a numeric constant without any description. Prefer named identifiers instead. Named identifiers make the code easier to read and easier to maintain.
For example, say you have a text field that has a capacity of 23 characters. If you change the capacity to 32, you will have to find every occurrence of 23, figure out if the instance applies to the text field, then modify them. With a named identifier, you would only make 1 change, and that is where the identifier is declared.
Use a debugger
Invest time now to learn to use a debugger. You'll find issues a lot faster on you own rather than having to wait for somebody to answer your post.
Use test cases
Design your program so it can read test cases. Place your data into a file and feed it to your program. Create test cases that pass. Create invalid test cases, these are the ones that will break your program. Can your program handle invalid input?
The test cases allow for automated testing your program. They also allow for consistent operation while you are debugging issues.
Edit 1: Comparing strings
Example:
std::string s1 = "Taco";
std::string s2 = "Salad";
if (s1 == s2)
{
cout << "s1 == s2\n";
}
if (s1 == "burrito")
{
cout << "s1 == burrito\n";
}
As #NathanOliver already pointed out in the comments, you need to open the file in binary mode in order to read bytes and put them in the student1 variable
file.open("student.dat",std::ios::in | std::ios::binary);
and correct the inner code of
displayStudent()
as it will not display the desired results

What is wrong with this code of file handling in c++?

I am new to c++ and specifically file handling.
I made this code.
#include <iostream>
#include <fstream>
using namespace std;
class student
{
public:
char s;
int age;
};
int main (void)
{
student a;
a.s = 'a';
a.age = 1;
ofstream myfile;
myfile.open("a1.txt",ios::app);
myfile.write(reinterpret_cast<char*>(&a),sizeof(student));
return 0;
}
When I opened the file a1.txt, it had the character a correctly in there, but for the integer it had some weird encoding and a message that if you continue to edit this file, it will be corrupted. Can't I write an object to a file containing an integer and a character or a string as well?
You're writing the binary representation of student into the file. The character will come out as expected; but the int will be the bytes used to represent the value, not a readable number.
If you want the output to be formatted as readable text, use formatted output:
myfile << a.s << ' ' << a.age << '\n';
For convenience, you could overload the operator for your class:
ostream & operator<<(ostream & os, student const & a) {
return os << a.s << ' ' << a.age;
}
myfile << a << '\n';
For more complex structures, you might consider the Boost.Serialization library. Or you might do what I tend to do, with tuples instead of plain structures, and variadic templates to read and write them, but that might be rather more fiddly than you'd like.
I would define your own << operator that handles your custom type.
#include <iostream>
#include <fstream>
using namespace std;
class student
{
char s;
int age;
};
ostream& operator<<(ostream &output, const student &o)
{
output << o.s << " " << o.age;
return output;
}
int main (void)
{
student a;
a.s = 'a';
a.age = 1;
ofstream myfile;
myfile.open("a1.txt",ofstream::app);
ofstream << a;
return 0;
}
When you called myfile.write(reinterpret_cast<char*>(&a),sizeof(student)); it was not converting your struct to a human-readable string then writing it to file. In reality, it was interpreting the memory of your struct as a series of characters then writing it to file.
You actually did write the int to the file, but not readable (123 => '1' '2' '3')
but the 4 (or 8) byte of that int. Your program can read the value back too,
the only probem is that we humans can´t read that form well.
Concering Strings:Just writing the whole struct will probably fail
(depending on the exact type of the string variable etc.), because the
"string" often stores only a pointer in the struct (which points to some
memory elsewhere, and this other memory isn´t written automatically to the file)
To be safe, write each variable of the struct explicitely (and handle different
var type appropiately) instead of writing them all together.
This way, things like different variable ordering and struct padding
can´t cause problems too. Other pitfalls to remember are different int
sizes and endianess on different computers... serialization isn´t trivial.

How to read and write a STL C++ string?

#include<string>
...
string in;
//How do I store a string from stdin to in?
//
//gets(in) - 16 cannot convert `std::string' to `char*' for argument `1' to
//char* gets (char*)'
//
//scanf("%s",in) also gives some weird error
Similarly, how do I write out in to stdout or to a file??
You are trying to mix C style I/O with C++ types. When using C++ you should use the std::cin and std::cout streams for console input and output.
#include <string>
#include <iostream>
...
std::string in;
std::string out("hello world");
std::cin >> in;
std::cout << out;
But when reading a string std::cin stops reading as soon as it encounters a space or new line. You may want to use std::getline to get a entire line of input from the console.
std::getline(std::cin, in);
You use the same methods with a file (when dealing with non binary data).
std::ofstream ofs("myfile.txt");
ofs << myString;
There are many way to read text from stdin into a std::string. The thing about std::strings though is that they grow as needed, which in turn means they reallocate. Internally a std::string has a pointer to a fixed-length buffer. When the buffer is full and you request to add one or more character onto it, the std::string object will create a new, larger buffer instead of the old one and move all the text to the new buffer.
All this to say that if you know the length of text you are about to read beforehand then you can improve performance by avoiding these reallocations.
#include <iostream>
#include <string>
#include <streambuf>
using namespace std;
// ...
// if you don't know the length of string ahead of time:
string in(istreambuf_iterator<char>(cin), istreambuf_iterator<char>());
// if you do know the length of string:
in.reserve(TEXT_LENGTH);
in.assign(istreambuf_iterator<char>(cin), istreambuf_iterator<char>());
// alternatively (include <algorithm> for this):
copy(istreambuf_iterator<char>(cin), istreambuf_iterator<char>(),
back_inserter(in));
All of the above will copy all text found in stdin, untill end-of-file. If you only want a single line, use std::getline():
#include <string>
#include <iostream>
// ...
string in;
while( getline(cin, in) ) {
// ...
}
If you want a single character, use std::istream::get():
#include <iostream>
// ...
char ch;
while( cin.get(ch) ) {
// ...
}
C++ strings must be read and written using >> and << operators and other C++ equivalents. However, if you want to use scanf as in C, you can always read a string the C++ way and use sscanf with it:
std::string s;
std::getline(cin, s);
sscanf(s.c_str(), "%i%i%c", ...);
The easiest way to output a string is with:
s = "string...";
cout << s;
But printf will work too:
[fixed printf]
printf("%s", s.c_str());
The method c_str() returns a pointer to a null-terminated ASCII string, which can be used by all standard C functions.