I'm practicing with c string / pointers and can't solve this simple problem. cin.getline() isn't prompting user input for the first iteration, but does so for the consecutive strings. (I arbitrarily chose 10 as the char limit).
Am I overlooking something extremely simple?
void getStrings() {
int num;
cout << "How many strings? ";
cin >> num;
const int numStrings = num;
char** stringSet = (char**) malloc(numStrings * sizeof(char*));
for (int i = 0; i < numStrings; i++) {
*(stringSet + i) = (char*) malloc(10);
cout << "String " << i << ": ";
cin.getline(stringSet[i], 10);
cout << endl;
}
Setting aside the fact that it's generally inadvisable to use bare pointers in C++ when things like the standard library's std::string are available, you should not use malloc. For example: Instead of (char*) malloc(10), you should write new char[10] and remember to delete[] *(stringSet+i) at the end of your program.
That said, the line:
cin >> num
... extracts only the first number it comes across. It will fail (and cin will set its fail bit, and will need to be reset with cin.reset()) if it encounters any non-whitespace characters before it encounters a number.
But it stops extracting from the input after that. In your input stream is still whatever whitespace or other characters were still present in your input. For example, if you ran this program and typed "2 foobar" before pressing enter, it would immediately print:
String 1: foobar
String 2:
This is because the stream still contains "foobar\n".
In order to get the behavior you're looking for you will probably want to add this before your loop:
cin.ignore();
That will clear the stream of anything that's there.
cin >> num;
This will prompt the user for some input. Assuming the user does what's expected of them, they will type some digits, and they will hit the enter key. The digits will be stored in the input buffer, but so will a newline character, which was added by the fact that they hit the enter key. cin will parse the digits to produce an integer, which it stores in the num variable. It stops at the newline character, which remains in the input buffer. Later, you call getline, which looks for a newline character in the input buffer. It finds one immediately, so it doesn't need to prompt the user for any more input. So it appears that the first call to getline didn't do anything, but actually it did.
What do you mean by " isn't prompting user input for the first iteration"? I read that to mean "isn't printing the prompt for the input of the first string", but based on your code, I think it means "is printing the prompt for the input of the first two strings before it reads input."
cin >> num reads a number, the whole number, and nothing but the number. It does not read whatever follows the number, like a newline. So the first getline reads the newline character which you've already typed.
By the way, you should use cerr instead of cout for user prompts.
cout should be reserved for actual output. That makes scripting much easier, because you can redirect cout independent of cerr, and because you don't want to capture prompts in the program results anyway.
Related
I am trying out dynamic memory allocation. I want to dynamically allocate memory, enough for a string of 10 characters. If the user inputs more than 10, I only want 10 characters pulled in getline (plus the newline character, I suppose) and subsequently stored in correct size dynamic memory, printed, and deleted. Below is my code:
#include <iostream>
using namespace std;
int main() {
char cont = 'N';
do{
char *input = new char[11];
cout << "This program dynamically allocates memory for a string of
characters up to 10 in length\n";
cout << "Enter a string less than or equal to 10 characters:\n";
cin.getline(input, 11, '\n');
cout << "Your string is:\n";
cout << input << endl << endl;
delete input;
cout << "Would you like to enter another? \"Y\" to continue\n";
cin >> cont;
cin.ignore();
} while (toupper(cont) == 'Y');
Everything works fine if the user inputs up to 10 characters. It can repeat forever.
However if the user inputs more than 10 characters the program does not prompt the user to continue and instead just ends. ABCDEFGHIJ works but ABCDEFGHIJK breaks.
I thought this is because there is still something in cin that is being read in to input, which is not 'Y' and the program therefore ends. However if you go through one loop and set cont to 'Y' first, and then input ABCDEFGHIJK, the program goes infinite without ever inputting anything to overwrite 'Y'.
Another thing I noticed is getline seems to pull the correct amount of characters it stores characters ABCDEFGHIJ in input, even if ABCDEFGHIJK was entered.I thought perhaps 'K' was left in cin, so I tried ABCDEFGHIJY but 'Y' was not saved in to cont.
I also tried various cin.clear() variations before the next prompt for input, and nothing worked.
I think I am misunderstanding how getline works with the overload I am using.
First let me tell you, that doing this with a string is a ton easier, but if you want to do it the hard, allocating way...
Problem:
When you read with std::cin, and some condition isn't met, for example, reading a string as an int, or running out of specified characters, the failbit is set. This makes your istream look crazy
Fix:
Put a check after the getline call, test if the fail bit is set, if it is, reset the flags.
/* ... */
cin.getline(input, 11, '\n'); // Read input
if ( cin.fail() ) { // Check if getline failed
// -- > Remind the user of the limits here < --
// Clear flags
cin.clear();
cin.ignore();
}
/* ... */
Your getline variation has the arguments: char_type * Output, std::streampos MaxLength, char_type Delimeter. Sets failbit if the number of bytes to read is greater than the maximum length (AFAIK).
Sources:
std::basic_istream::fail()
std::basic_istream::getline()
Wish luck with your dynamic allocations, COlda
It must be that I'm tired but I can't seem to figure out why my program will not read my cin values for init and end which is supposed to be a start and an end point for my program. The way its setup for input is the first line is the amount of cases then there will be a new line which will be a string then on the next line there will be two integers separated by a space so I feel the way I built it is correct but when I debug the code the values for init and end never change whether I initialize the variable or not.
int case_count;
string name;
int init = 0;
int end = 0;
cin >> case_count;
cin.ignore(numeric_limits<::streamsize>::max(),'\n');
for (int i = 0; i < case_count; ++i) {
cout << "Case " << i << "\n";
getline(cin, name);
cin.ignore(numeric_limits<::streamsize>::max(), '\n');
cin >> init;
cin >> end;
Example input
3
flap
1 3
swoop
0 9
flip
0 6
Its making it hard to continue with the rest of the code.
The problem is that you call ignore in the loop. The problem with this is that std::getline extracts (and throws away) the newline character, meaning your ignore call in the loop will read and discard the next line (the one with the two numbers).
Besides that, when using the input operator to read numbers or strings, the operator will skip leading white-space, so even if getline didn't read the newline, it would still have worked without the ignore call.
However, there is a newline left over in the input stream after you read the numbers, and the next iteration the std::getline call will pick up that newline and then name will be empty, so you should, as suggested by M.M in a comment, move the ignore call to last in the loop.
I currently have a function that takes a array of 4 characters and returns another value based on that sequence of characters.
What I want is to have the user input a whole line of characters and then create a loop to go over each "sub- group of characters" and then return the result for all of them.
My initial thinking is to somehow use push_back to keep adding the arrays to a vector.
I don't know how long the entire array will be, but it should be a product of 3.
As an example, right now I am able to do :
char input [4] ;
cin >> input;
int i = name_index[name_number(input)];
cout << name[i].fullName;
But what I would like is the user ti input multiple name abbreviations at once
I would change your sample from this:
char input [4] ;
cin >> input;
int i = name_index[name_number(input)];
cout << name[i].fullName;
To this:
string input;
cin >> input;
const int i = name_index[name_number(input)];
cout << name[i].fullName;
Then you can start using a vector to track multiple inputs:
vector<string> inputs;
string line;
while (cin >> line)
{
if (line == "done")
break;
inputs.push_back(line);
}
for (unsigned int i = 0; i < inputs.size(); ++i)
{
cout << "inputs[" << i << "]: " << inputs[i] << endl;
//const int index = name_index[name_number(inputs[i])];
//cout << name[index].fullName;
}
You asked for an explanation of line. The line while (cin >> line) tries to take text from the standard input and put it into line. By default, this will stop when it encounters whitespace (space, tab, return, etc.) If it succeeds, then the body of the while loop is executed and we add what was input to the vector. If not, then we assume we're at the end of input and stop. We can then process the vector. (In the code linked below, I just output it since I don't know what name_index or name_number are.
(Working code here)
The way cin works, is it will accept any amount of input and separate them each by spaces, when you ask for specific input it prompts the user to give input, and then only takes the very first string (up until the space). If there are any more input after that, another cin >> input will just retrieve that value without prompting the user again. You can tell when the actual end of the input is reached when there is only a newline character left. This code should allow you to type in multiple strings separated by spaces and then process them all at once after the user enters the text:
char input[4];
do // Start our loop here.
{
// Our very first time entering here, it will prompt the user for input.
// Here the user can enter multiple 4 character strings separated by spaces.
// On each loop after the first, it will pull the next set of 4 characters that
// are still remaining from our last input and use it without prompting the user
// again for more input.
// Once we run out of input, calling this again will prompt the user for more
// input again. To prevent this, at the end of this loop we bail out if we
// have come accros the end of our input stream.
cin >> input;
// input will be filled with each 4 character string each time we get here.
int i = name_index[name_number(input)];
cout << name[i].fullName;
} while (cin.peek() != '\n'); // We infinitely loop until we reach the newline character.
EDIT: Also, keep in mind that allocating only 4 characters to input does not account for the end of string character '\0' that will be tacked on the end. If the user inputs 4 characters then it will actually access bad memory when it assigns the string to your input. There are 4 characters + 1 end character which means you need a minimum of 5 characters allocated for your input. The best way is to just use std::string as it will size itself properly even if the user enters more than 4 characters.
I've come this far without asking for help, but I've got a problem that I can't seem to fix. I like cryptology, so now that I am learning C++, I want to make programs to encrypt and decrypt strings. I read that the best way is to convert the text to ASCII and go from there, so here is a simple program I made in C++ to try and convert a char variable to ASCII:
#include <iostream>
#include <string>
#include <math.h>
using namespace std;
int main()
{
char strString[1000];
cout<<"Enter you message:"<<endl;
cin>>strString[1000];
string strEncrypt;
int a = 0;
while (strString != '\0')
{
int b = (int)strString[a];
strEncrypt.at(a) = b; //This is where I'm getting an error.
a++;
}
cout<<"Encrypted message:"<<endl;
cout<<strEncrypt<<endl;
}
So, I've tried all 3 things I know to do to troubleshoot (Google, check for missing simicolons, and make sure I'm doing == not =, but this is just something I don't know how to do, not something I'm forgetting (I hope). So, any help would great!
You don't have to change the characters to ASCII they already are. Chars are basically the same as integers in memory.
Now to your question; . If you want to set a character in a string you can do that like this
string[index] = b;
Another thing to be careful for in your code. You are using cin to read the string from the user. This will not let you read messages that have spaces in them and will only read the first word. For example, if the user enters "Love Crypto" cin will only read "Love" and "Crypto" will be ignored. To get the entire line, use getline instead.
As for looping over characters in a string, it's better to do it as follows:
for(int i = 0; i < strString.length(); i++)
{
strString[i] = bla;
}
Again, you're code isn't actually doing anything. It is only reading a letter and then storing a "letter" in another string.
string::at() throws exception if the index passed to at() is out of range. So, if you are getting runtime error then it's expected. Because, your string strEncrypt is initialized to "" and thus the size is 0.
You may try
strEncrypt.reserve(strlen(strString));
Easiest way to actually make the code you have work is change this line strEncrypt.at(a) = b; to this strEncrypt += b; Which will add the characters to the empty string strEncrypt.
Your code doesn't make much sense though as char types are already ascii. You'll have to explain more about what kind of encrypting you are trying to do and maybe we can point you in the right direction.
EDIT: After thinking about what you're trying to do a bit more based on the code you have it seems like you want to print the numeric ascii value of characters. You can do that with just a cast like this:
string input;
cout << "Enter you message:" << endl;
// handle spaces in the message
getline(cin, input);
cout << "String chars as ascii values:" << endl;
cout << "Char: " << "ASCII Code:" << endl;
for (int i = 0; i < input.length(); ++i)
{
// casting the char to an int with (int) will print the ascii code
cout << input[i] << " " << (int)input[i] << endl;
}
On top of the fact that your input is already in ASCII, keep in mind that doing cin >> strString[1000] doesn't limit the input captured to the length of your buffer unless you specifically specify the number of characters to capture for the stream object using setw() or setting it's ios_base::width data member. So your method right now risks buffer overflows.
Secondly, the form of cin >> that you're using will not capture the entire line of input. Instead it will stop at the first white-space or any other delimiting character (or end-of-file if that is reached first). In your case, if you are entering a line like "Hello World", then the syntax you're using will only capture "Hello" and drop "World".
A much better idea would be to use the getline() function with a std::string object if you are wanting to capture a line of input to a string and remove the delimiting newline character without risking buffer overflows ... for instance:
string strString;
getline(cin, strString);
Apart from advises given, when receiving this kind of run-time errors use Cppcheck utility.
It will give you the answer: "Message: Array 'strString[1000]' index 1000 out of bounds".
I am having a problem, but I cannot figure out what I'm doing wrong. I'm not sure if it's a problem with my loop, or the cin buffer is not being cleaned. I am doing a program that transforms a C-style string to uppercase, however if the user enters more than 11 characters, then the function should only display the first 11, and anything after that should not be displayed.the problem is that if I enter more than 11 characters, then my loop never stops and keeps telling the user that the answer entered is invalid and if he would like to enter a new string.
The issue comes from when you're trying to clear your buffer. When you return from cStringToUpper there are still extra characters in your buffer, but you're immediately looking for y/q.
You give cin.getline a buffer 12 long so it will only take that many characters, the rest are still in the buffer. If you instead use
string str;
cin.getline(str)
Then you will get the whole line, then you can crop it at 11 characters. Not 100% on the cin-syntax but you get the idea.
Or move the ignore-part above
cin >>cont;
to ignore the extra characters that way.
cin >> cont;
cout << "\n" << endl;
cin.ignore(200,'\n');
should be
cin.ignore(200,'\n');
cin >> cont;
cout << "\n" << endl;
You may correct your program by modifying your cStringToUpper fn. something like:
...
int loopCount;
char buffer[256];
cin.getline(buffer,256);
strncpy(letters, buffer, 11);
//letters[11]= '\0';
cout << "\n" << endl;
...