Pointers to structures - having trouble understanding specific fragments of code - c++

Here is the code:
// pointers to structures
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
struct movies_t {
string title;
int year;
};
int main ()
{
string mystr;
movies_t amovie;
movies_t * pmovie;
pmovie = &amovie;
cout << "Enter title: ";
getline (cin, pmovie->title);
cout << "Enter year: ";
getline (cin, mystr);
(stringstream) mystr >> pmovie->year;
cout << "\nYou have entered:\n";
cout << pmovie->title;
cout << " (" << pmovie->year << ")\n";
return 0;
}
Taken from http://www.cplusplus.com/doc/tutorial/structures/. I was hoping I could get clarification on a few things.
What is getline and how does it work? I tried looking up the documentation, but I'm still having trouble understanding. Also, what exactly is cin and how is it being used with getline?
If I understand correctly, pmovie->title essentially says that pmovie points to the member title of the object amovie? If so, and it's not already clear from the explanation to #1, how does getline (cin, pmovie->title) work?
Now this (stringstream) mystr >> pmovie->year is giving me the most trouble. What is a stringstream, and are we using it like we would cast a double as a int, for example?
Thank you all!

What is getline and how does it work? I tried looking up the documentation, but I'm still having trouble understanding. Also, what exactly is cin and how is it being used with getline?
The getline function reads a line from a istream. The cin stream refers to your standard input stream, the one you would normally get input from. It is being passed to getline to tell it which input stream to get a line from.
If I understand correctly, pmovie->title essentially says that pmovie points to the member title of the object amovie? If so, and it's not already clear from the explanation to #1, how does getline (cin, pmovie->title) work?
The getline functions reads a line from cin and stores it in pmovie->title which is passed by reference.
Now this (stringstream) mystr >> pmovie->year is giving me the most trouble. What is a stringstream, and are we using it like we would cast a double as a int, for example?
A stringstream is a class that makes a string act like a stream. This is kind of confusing syntax (C-style cast) that makes it a bit harder to understand what it is happening. Basically, a temporary stringstream is created and initialized with the contents of mystr. A stringstream, when initialized with a string, gives you a stream from which you can read those contents. The >> operator reads from an output stream, in this case, into pmovie->year, which is again passed by reference.
By the way, it seems to me like you're trying to understand unusually complex and confusing uses without yet understanding the more basic uses of these objects. That's a very hard way to learn.

Most of the questions don't seem to be about structures at all. So, I'm addressing the issue which is related to the title rather than those about streams:
If I understand correctly, pmovie->title essentially says that pmovie points to the member title of the object amovie? If so, and it's not already clear from the explanation to #1, how does getline (cin, pmovie->title) work?
You misunderstand. I would guess, that this is the root of your confusion: pmovie points to a movies_t object. As it happens, in the sample code it is initialized to point to the movies_t object named amovie.
Now, each movies_t object has two members, i.e., subobjects: a title and a year. To access the title component of a movies_t pointed to by a pointer you use pmovie->title. To access the year component instead you'd use pmovie->year.
The one thing I say about streams, though, is this: You should always check that your input was successful before assuming the read was successful. For example, you would check that reading a line was successful using
if (std::getline(std::cin, pmovie->title)) {
// deal with a successfully read title
}

cin is a special stream defined by C++ to work with standard output (usually the keyboard, but can be almost anything). getline is a function that allows you to read text from a stream into a buffer until the platform's line ending is encountered (Line Feed on UNIX, Carriage Return Line Feed of Windows and DOS).
pmovie->title says that pmove is a pointer to a structure that has a member called title. This refers to that member. Because getline takes a string& (String reference), it happily accepts the string referenced by pmovie->title.
stringstream defines an implicit constructor that converts strings to stringstreams. the >> operator gets input from a string and converts it to the target type (the type of the operand to the right of the >>) and puts it there. This is just a way of converting a string to an integer.

Related

Declaration of a String of Dynamic Length Using Pointer

I wanted to declare an array with a pointer in character type, and the length of the array can be determined by my input string.
I wrote it in this way:
char *s;
cout << "Enter a string: " << endl;
cin >> s;
I expected that I can initialize the string by the cin operation, but an error showed up when compiling. The error is about "invalid operands to binary expression".
I'm not sure why the lines I wrote was wrong.
I though not only the built in string class is used for declaring an array.
Isn't the string data type in C++ the same as "a character array"?
Isn't the line char *s means the pointer s points to an character array (or string)?
Thank you!
You should use std::string.
It is a class that represents a string of characters. It is different than an old c style array of characters (although internally might contain one).
In your case:
#include <string>
#include <iostream>
std::string s;
std::cout << "Enter a string: " << endl;
std::cin >> s;
Using std::string means memory is managed automatically for you. Specifically with cin it will also be resized to fit the input.
A side note: better to avoid using namespace std - see here Why is "using namespace std;" considered bad practice?.
"the cin operation".
cin is really the source. The real work is done by the overloaded operator>>. And the operator>> which reads to a char* expects that the char* is already allocated to the right size. That's of course a problem with cin, where the size is unknown.
The operator>> overload that reads to std::string will resize the std::string to the right size.
The answer to your question is no, as when you create a type pointer you always have to specify in advance how much memory to allocate. We can imagine that this is what happens with strings, that is to go to fetch the data and arrange the occupied cells in memory at a later time.
Now the real problem is, it is true that you have declared a pointer to a character, but you have not specified how much to allocate for it. It is as if you are saying you want to create a box but you are not specifying the size. I show you the correct method:
char *s = new char[10];
Obviously when using pointers, always remember to deallocate them at the end of use so as not to have any memory leaks.
Taking a summary of the situation, you tried to save a data in a box that you intend to create but does not exist. That is, you have named the box called s which will contain a pointer to a character but you have not yet built/created it in its final size.

How to divide the input taken from user in two parts and assign them to two different arrays in C++?

If we take the input from user by asking them, like below:
cout << "Enter your course code and course name: ";
Now if the user enters CS201 Introduction to Programming, how can I only assign the code part, i.e. CS201 to an array, let's say;
char courseCode[10];
And how can I assign the name part in the array, let's say:
char courseName[50];
I want to do this to 5 students, using the structure defined below:
struct student
{
char courseName[50];
char courseCode[10];
};
student stu[5];
It's actually kind of simple once you remember that the input operator >> stops on white-space, and also know about the std::getline function.
Then you can do something like
std::string courseCode;
std::string courseName;
std::cin >> courseCode;
std::getline(std::cin, courseName);
Note that I use std::string for the strings instead of arrays. This is what you really should use. If you're not allowed (by your teacher or something) and must use arrays, then you can't use std::getline but instead have to use std::istream::getline instead.
Store the input in a single string say x
Now on x perform linear search for the first whitespace and split the string about the first whitespace. Store the two resultant strings in your struct.
I solved my problem using the cin.getline() functions to get the string in token pointer and then used strchr(char [], cahr) of <string> header file to separate the current string from the place where the first white space comes. Then I copied both separated strings into my desired elements of my structure using strcpy() function.

How do the different ways to read strings from console actually differ? Operator <<, getline or cin.getline?

Let's suppose I'd like to read an integer from the console, and I would not like the program to break if it is fed non-integer characters. This is how I would do this:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main() {
string input; int n;
cin >> input;
if(!(stringstream(input)>>n)) cout << "Bad input!\n";
else cout << n;
return 0;
}
However, I see that http://www.cplusplus.com/doc/tutorial/basic_io/ uses getline(cin,input) rather than cin >> input. Are there any relevant differences between the two methods?
Also I wonder, since string is supposed not to have any length limits... What would happen if someone passed a 10GB long string to this program? Wouldn't it be safer to store the input in a limited-length char table and use, for example, cin.getline(input,256)?
std::getline gets a line (including spaces) and also reads (but discards) the ending newline. The input operator >> reads a whitespace-delimited "word".
For example, if your input is
123 456 789
Using std::getline will give you the string "123 456 789", but using the input operator >> you will get only "123".
And theoretically there's no limit to std::string, but in reality it's of course limited to the amount of memory it can allocate.
the first gets a line,
the second gets a world.if your input "hello world"
getline(cin,str) : str=="hello world"
cin>>str: str="hello"
and dont worry about out of range, like vector ,string can grow
operator>> reads a word (i.e. up to next whitespace-character)
std::getline() reads a "line" (by default up to next newline, but you can configure the delimiter) and stores the result in a std::string
istream::getline() reads a "line" in a fashion similar to std::getline(), but takes a char-array as its target. (This is the one you'll find as cin.getline())
If you get a 10 GB line passed to your program, then you'll either run out of memory on a 32-bit system, or take a while to parse it, potentially swapping a fair bit of memory to disk on a 64-bit system.
Whether arbitrary line-length size limitations make sense, really boils down to what kind of data your program expects to handle, as well as what kind of error you want to produce when you get too much data. (Presumably it is not acceptable behaviour to simply ignore the rest of the data, so you'll want to either read and perform work on it in parts, or error out)

How to name text file at a certain directory?

Ok thanks for the answer Wug! I changed my code but now it's complaining about:
no matching function for call to
std::basic_ofstream::basic_ofstream(std::basic_string)
I'm not sure it makes any difference but i'll just post all of my code it's not that much so far.
I'll try to keep it cleaner from now on.
#include <iostream>
#include <windows.h>
#include <direct.h>
#include <fstream>
using namespace std;
int main()
{ /*Introduction*/
SetConsoleTitle("Journal");
string action, prom0, filename, filepath;
filepath = "C:\\Users\\-\\Desktop\\Projects\\Journal Project\\Logs\\";
cout << "Hi and welcome to Journal! \nHere you can write down your day.\nWrite help for";
cout << "more \nType command to start: ";
/*Choose Action*/
cin >> action;
if (action == "new")
{system("cls");
/*Make new Journal file*/
cout << "Filename: ";
getline(cin, filename);
mkdir("C:\\Users\\-\\Desktop\\Projects\\Journal Project\\Logs");
ofstream journallogs(filepath + filename);
journallogs.close();
}
else {
cout << "Wrong command\n";
};
return 0;}
There are 2 things wrong. The first is what the compiler's complaining about:
ofstream journallogs("C:\\Users\\-\\Desktop\\Projects\\Journal Project\\Logs\\" + getline(cin, filename), ios::out);
std::getline(istream&, string&) returns istream&, and you can't add char * to istream. I recommend taking a look at the documentation for getline(), which might help you understand better how you're supposed to use it. Here's an example anyway:
string filepath = "C:\\Users\\-\\Desktop\\Projects\\Journal Project\\Logs\\";
string filename;
getline(cin, filename);
ofstream journallogs(filepath + filename);
The second problem is that you're reading from cin into filename before calling getline(). When you call getline(), any contents of filename are dropped, so you'll effectively trim the first word off of your filename, which probably isn't what you want. To fix that, remove the extraneous cin >> filename;
Note: indentation is important and helps you read your own code. Put forth the effort to keep your code looking nice.
First, learn this:
Start small and simple.
Add complexity a little at a time.
Test at every step.
develop new functionality in isolation.
Never add to code that doesn't work.
For the rest, I don't use Windows, so I can't be certain my code will work there, but the approach will.
You are trying to 1) get a filename from the user, 2) modify it and then 3) use it to open a file; we will develop these three things in isolation.
Getting a filename from the user. Civilized filenames do not contain whitespace, so they can be read with cin, but if you want to allow whitespace you can use getline instead. Either way, test it.
Modifying the filename. Write code that assigns a value to the filename, just as it does to the path-- do not get the filename from the user, it slows down your testing and is not proper isolation. Now try to append them. If you try filepath + filename, you may get a compiler error. Here's where you must understand the difference between std::string and char[]. A char[] is an array of char, and it (usually) contains a null-terminated sequence of characters; you must read up on arrays and pointers. It is a primitive type, and you cannot simply concatenate two of them with '+', you must use something like strcat, which is dangerous if you haven't done your homework on arrays. On the other hand, std::string is more sophisticated, and can handle '+' and many other operations. If you have a std::string x and you decide you want a char[] after all, you can get one like so: x.c_str().
Opening the file. If I remember right, the ofstream constructor can take a char[], but not a std::string. Test this with a hard-coded string (isolation!).
Once you have these three components working independently, you can hook them together.

Reading a fixed number of chars with << on an istream

I was trying out a few file reading strategies in C++ and I came across this.
ifstream ifsw1("c:\\trys\\str3.txt");
char ifsw1w[3];
do {
ifsw1 >> ifsw1w;
if (ifsw1.eof())
break;
cout << ifsw1w << flush << endl;
} while (1);
ifsw1.close();
The content of the file were
firstfirst firstsecond
secondfirst secondsecond
When I see the output it is printed as
firstfirst
firstsecond
secondfirst
I expected the output to be something like:
fir
stf
irs
tfi
.....
Moreover I see that "secondsecond" has not been printed. I guess that the last read has met the eof and the cout might not have been executed. But the first behavior is not understandable.
The extraction operator has no concept of the size of the ifsw1w variable, and (by default) is going to extract characters until it hits whitespace, null, or eof. These are likely being stored in the memory locations after your ifsw1w variable, which would cause bad bugs if you had additional variables defined.
To get the desired behavior, you should be able to use
ifsw1.width(3);
to limit the number of characters to extract.
It's virtually impossible to use std::istream& operator>>(std::istream&, char *) safely -- it's like gets in this regard -- there's no way for you to specify the buffer size. The stream just writes to your buffer, going off the end. (Your example above invokes undefined behavior). Either use the overloads accepting a std::string, or use std::getline(std::istream&, std::string).
Checking eof() is incorrect. You want fail() instead. You really don't care if the stream is at the end of the file, you care only if you have failed to extract information.
For something like this you're probably better off just reading the whole file into a string and using string operations from that point. You can do that using a stringstream:
#include <string> //For string
#include <sstream> //For stringstream
#include <iostream> //As before
std::ifstream myFile(...);
std::stringstream ss;
ss << myFile.rdbuf(); //Read the file into the stringstream.
std::string fileContents = ss.str(); //Now you have a string, no loops!
You're trashing the memory... its reading past the 3 chars you defined (its reading until a space or a new line is met...).
Read char by char to achieve the output you had mentioned.
Edit : Irritate is right, this works too (with some fixes and not getting the exact result, but that's the spirit):
char ifsw1w[4];
do{
ifsw1.width(4);
ifsw1 >> ifsw1w;
if(ifsw1.eof()) break;
cout << ifsw1w << flush << endl;
}while(1);
ifsw1.close();
The code has undefined behavior. When you do something like this:
char ifsw1w[3];
ifsw1 >> ifsw1w;
The operator>> receives a pointer to the buffer, but has no idea of the buffer's actual size. As such, it has no way to know that it should stop reading after two characters (and note that it should be 2, not 3 -- it needs space for a '\0' to terminate the string).
Bottom line: in your exploration of ways to read data, this code is probably best ignored. About all you can learn from code like this is a few things you should avoid. It's generally easier, however, to just follow a few rules of thumb than try to study all the problems that can arise.
Use std::string to read strings.
Only use fixed-size buffers for fixed-size data.
When you do use fixed buffers, pass their size to limit how much is read.
When you want to read all the data in a file, std::copy can avoid a lot of errors:
std::vector<std::string> strings;
std::copy(std::istream_iterator<std::string>(myFile),
std::istream_iterator<std::string>(),
std::back_inserter(strings));
To read the whitespace, you could used "noskipws", it will not skip whitespace.
ifsw1 >> noskipws >> ifsw1w;
But if you want to get only 3 characters, I suggest you to use the get method:
ifsw1.get(ifsw1w,3);