In C++ while using cin.get(), how can I not limit the number of characters a user can enter? - c++

Im trying to get a users input using cin.get() but I dont want to limit the amount of characters that they can enter. How can I do this?
EDIT: I guess a better way to phrase this would be: How can I dynamicaly change the character array to fit the length of the users input?

This is a strange requirement for a C++ program. You can of course go the C way and simply keep on getting more memory whenever your input outgrows the currently available memory. It goes something like this (warning: code fragments ahead):
while(cin.get(c)) {
if (cur_pos == cur_len) {
cur_len = grow_charbuf(buffer, cur_len);
}
buffer[cur_pos++] = c;
}
Here, the grow function is where it gets ugly. It needs to allocate a larger piece of memory, copy the contents of the current buffer to the beginning of that, dealocate the memory occupied by the current buffer, and return the new size. For example, something along these lines:
char* new_charbuf(size_t len) {
return new char [len];
}
size_t grow_charbuf(char* buf, size_t cur_len) {
size_t new_len = cur_len * 2;
char* new_buf = new char [new_len];
// copy old buffer contents to new buffer
delete[] buf;
buf = new_buf;
return new_len;
}
And you can then use it as follows:
cur_len = 1000; // or whatever
char* buffer = new_charbur(cur_len);
// write into the buffer, calling grow_charbuf() when necessary
// and don't forget to free the memory once you are done...
// or don't free it, if the program eventually exits anyway
This is terrible code. It might work, but you should never ever do this in C++ if you can avoid it. Apart from this, I have avoided handling any error conditions or exceptions that this code might cause. It is meant just to illustrate the idea.
Managing your memory manually is a bad idea because it requires a lot of code and is not easy to get right. You can get away with less if your program has a known, limited life-span.

Don't use characters array at all. Use std::string or other standard containers.
And of cause learn to use streams.
Here an example. It reads as many characters as the user inputs until the user presses enter. As you cna see, there is no explicite buffer-size required:
/////TEST PUT ANYWHERE IN GLOBAL SCOPE
#include <string>
#include <sstream>
#include <iostream>
using namespace std;
int test()
{
//SET BP HERE AND STEP THROUGH
string line;
getline(cin,line);
std::stringstream user_input( line );
while(!user_input.eof())
{
string word;
user_input >> word;
cout << word << endl;
}
return 0;
}
static int _test = test();
/////END TEST

You need a cin.getline(). In other words you need to have a specified size of char array and use it like so:
Using cin.get()
char str[100];
char sayHello[100];
cin.get(str, 100);
// make sure to add cin.ignore() or program will terminate right before next cin().
cin.ignore();
cout << str << endl;
cin.get(sayHello, 100);
cout << sayHello;
or for cin.getline()
char input[100];
cin.ignore(); // stops the sentence from truncating.
cin.getline(input,sizeof(input));
You could also use getline() for strings like so:
string name;
getline(cin, name);
The problem is that in c++ when receiving input your cin looks for the 0 aka the space in your sentence. It then ends thinking that was the end.

Related

Replacements for deprecated strcpy/strcat when using char*

I have code for a 'zombie translator' as an example from my professor. From what I can tell it takes a string of english words and applies a few rules to it via functions. It currently uses strcpy and strcat to do this, however it will not compile even if I change them to strcpy_s. Without including the other functions (for the sake of space), here is my main function as an example
int main()
{
char english[MAX];
char zombie[MAX];
char zombie_word[MAX];
int pos_e; /* Current position in english line of text */
int pos_z; /* Current position in line of translated zombie text */
while (1) {
pos_e = 0;
pos_z = 0;
strcpy(zombie, "");
cout << ("Enter English text: ");
cin >> english;
/* This loop translates the line from english to zombie. */
do
{
get_next_word(english, &pos_e, zombie, &pos_z);
translate_word(english, &pos_e, zombie_word, &pos_z);
strcat(zombie, zombie_word);
} while (pos_e < strlen(english));
print_translation(zombie);
}
return 0;
}
So more specifically, what should i do to the line strcat(zombie, zombie_word); to make it compile properly in Visual Studio 2015?
It's not for a grade, I just really want to be able to understand this before the midterm, and it's a bit difficult to play around with it. I would prefer not to have to disable it through _CRT_SECURE_NO_WARNINGS so that I know what to do if I need to do something similar.
Perhaps changing the char variables into strings or something like that? I've been looking around for awhile and can't find the actual process.
Thank you very much for any assistance, I greatly appreciate your time.
From Microsoft: strncat_s
You need to include the length of the array to prevent buffer overflow dangers.
The API is:
errno_t strncat_s(
char *strDest,
size_t numberOfElements,
const char *strSource,
size_t count
);
numberOfElements is size of destination array.

Read a string with ncurses in C++

I'm writing a text-based game in C++. At some point, I ask the user to input user names corresponding to the different players playing.
I'm currently reading single char from ncurses like so:
move(y,x);
printw("Enter a char");
int char = getch();
However, I'm not sure how to a string. I'm looking for something like:
move(y,x);
printw("Enter a name: ");
std::string name = getstring();
I've seen many different guides for using ncurses all using a different set of functions that the other doesn't. As far as I can tell the lines between deprecated and non-deprecated functions is not very well defined.
How about this?
std::string getstring()
{
std::string input;
// let the terminal do the line editing
nocbreak();
echo();
// this reads from buffer after <ENTER>, not "raw"
// so any backspacing etc. has already been taken care of
int ch = getch();
while ( ch != '\n' )
{
input.push_back( ch );
ch = getch();
}
// restore your cbreak / echo settings here
return input;
}
I would discourage using the alternative *scanw() function family. You would be juggling a temporary char [] buffer, the underlying *scanf() functionality with all its problems, plus the specification of *scanw() states it returns ERR or OK instead of the number of items scanned, further reducing its usefulness.
While getstr() (suggested by user indiv) looks better than *scanw() and does special handling of function keys, it would still require a temporary char [], and I try to avoid those in C++ code, if for nothing else but avoiding some arbitrary buffer size.

C++ iostream binary read and write issues

Right, please bear with me as I have two separate attempts I'll cover below.
I first started off reading the guide here (http://www.cplusplus.com/doc/tutorial/files/). However whilst it contains what appears to be a good example of how to use read(), it does not contain an example of how to use write().
I first attempted to store a simple char array in binary using write(). My original idea (and hope) was that I could append to this file with new entries using ios::app. Originally this appeared to work, but I was getting junk output as well. A post on another forum for help suggested I lacked a null terminator on the end of my char array. I applied this (or at least attempted to based on how I was shown) as can be seen in the example below. Unfortunately, this meant that read() no longer functioned properly because it won't read past the null terminator.
I was also told that doing char *memoryBlock is 'abuse' of C++ standard or something, and is unsafe, and that I should instead define an array of an exact size, ie char memoryBlock[5], however what if I wish to write char data to a file that could be of any size? How do I proceed then? The code below includes various commented out lines of code indicating various attempts I have made and different variations, including some of the suggestions I mentioned above. I do wish to try and use good-practice code, so if char *memoryBlock is unsafe, or any other lines of code, I wish to amend this.
I would also like to clarify that I am trying to write chars here for testing purposes only, so please do not suggest that I should write in text mode rather than binary mode instead. I'll elaborate further in the second part of this question under the code below.
First code:
#include <cstdlib>
#include <iostream>
#include <fstream>
//#include <string>
int main()
{
//char memoryBlock[5];
char *memoryBlock;
char *memoryBlockTwo;
std::ifstream::pos_type size;// The number of characters to be read or written from/to the memory block.
std::ofstream myFile;
myFile.open("Example", std::ios::out | /*std::ios::app |*/ std::ios::binary);
if(myFile.is_open() && myFile.good())
{
//myFile.seekp(0,std::ios::end);
std::cout<<"File opening successfully completed."<<std::endl;
memoryBlock = "THEN";
//myFile.write(memoryBlock, (sizeof(char)*4));
//memoryBlock = "NOW THIS";
//strcpy_s(memoryBlock, (sizeof(char)*5),"THIS");
//memoryBlock = "THEN";
//strcpy(memoryBlock, "THIS");
//memoryBlock[5] = NULL;
myFile.write(memoryBlock, (sizeof(char)*5));
}
else
{
std::cout<<"File opening NOT successfully completed."<<std::endl;
}
myFile.close();
std::ifstream myFileInput;
myFileInput.open("Example", std::ios::in | std::ios::binary | std::ios::ate);
if(myFileInput.is_open() && myFileInput.good())
{
std::cout<<"File opening successfully completed. Again."<<std::endl;
std::cout<<"READ:"<<std::endl;
size = myFileInput.tellg();
memoryBlockTwo = new char[size];
myFileInput.seekg(0, std::ios::beg);// Get a pointer to the beginning of the file.
myFileInput.read(memoryBlockTwo, size);
std::cout<<memoryBlockTwo<<std::endl;
delete[] memoryBlockTwo;
std::cout<<std::endl<<"END."<<std::endl;
}
else
{
std::cout<<"Something has gone disasterously wrong."<<std::endl;
}
myFileInput.close();
return 0;
}
The next attempt of mine works on the basis that attempting to use ios::app with ios::binary simply won't work, and that to ammend a file I must read the entire thing in, make my alterations, then write back and replace the entire contents of the file, although this does seem somewhat inefficient.
However I don't read in and ammend contents in my code below. What I am actually trying to do is write an object of a custom class to a file, then read it back out again intact.
This seems to work (although if I'm doing anything bad code-wise here, please point it out), HOWEVER, I am seemingly unable to store variables of type std::string and std::vector because I get access violations when I reach myFileInput.close(). With those member variables commented out the access violation does not occur. My best guess as to why this happens is that They use pointers to other pieces of memory to store their files, and I am not writing the data itself to my file but the pointers to it, which happen to still be valid when I read my data out.
Is it possible at all to store the contents of these more complex datatypes in a file? Or must I break everything down in to more basic variables such as chars, ints and floats?
Second code:
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
class testClass
{
public:
testClass()
{
testInt = 5;
testChar = 't';
//testString = "Test string.";
//testVector.push_back(3.142f);
//testVector.push_back(0.001f);
}
testClass(int intInput, char charInput, std::string stringInput, float floatInput01, float floatInput02)
{
testInt = intInput;
testChar = charInput;
testArray[0] = 't';
testArray[1] = 'e';
testArray[2] = 's';
testArray[3] = 't';
testArray[4] = '\0';
//testString = stringInput;
//testVector = vectorInput;
//testVector.push_back(floatInput01);
//testVector.push_back(floatInput02);
}
~testClass()
{}
private:
int testInt;
char testChar;
char testArray[5];
//std::string testString;
//std::vector<float> testVector;
};
int main()
{
testClass testObject(3, 'x', "Hello there!", 9.14f, 6.662f);
testClass testReceivedObject;
//char memoryBlock[5];
//char *memoryBlock;
//char *memoryBlockTwo;
std::ifstream::pos_type size;// The number of characters to be read or written from/to the memory block.
std::ofstream myFile;
myFile.open("Example", std::ios::out | /*std::ios::app |*/ std::ios::binary);
if(myFile.is_open() && myFile.good())
{
//myFile.seekp(0,std::ios::end);
std::cout<<"File opening successfully completed."<<std::endl;
//memoryBlock = "THEN";
//myFile.write(memoryBlock, (sizeof(char)*4));
//memoryBlock = "NOW THIS";
//strcpy_s(memoryBlock, (sizeof(char)*5),"THIS");
//memoryBlock = "THEN AND NOW";
//strcpy(memoryBlock, "THIS");
//memoryBlock[5] = NULL;
myFile.write(reinterpret_cast<char*>(&testObject), (sizeof(testClass)));//(sizeof(char)*5));
}
else
{
std::cout<<"File opening NOT successfully completed."<<std::endl;
}
myFile.close();
std::ifstream myFileInput;
myFileInput.open("Example", std::ios::in | std::ios::binary | std::ios::ate);
if(myFileInput.is_open() && myFileInput.good())
{
std::cout<<"File opening successfully completed. Again."<<std::endl;
std::cout<<"READ:"<<std::endl;
size = myFileInput.tellg();
//memoryBlockTwo = new char[size];
myFileInput.seekg(0, std::ios::beg);// Get a pointer to the beginning of the file.
myFileInput.read(reinterpret_cast<char *>(&testReceivedObject), size);
//std::cout<<memoryBlockTwo<<std::endl;
//delete[] memoryBlockTwo;
std::cout<<std::endl<<"END."<<std::endl;
}
else
{
std::cout<<"Something has gone disasterously wrong."<<std::endl;
}
myFileInput.close();
return 0;
}
I apologise for the long-windedness of this question, but I am hoping that my thoroughness in providing as much information as I can about my issues will hasten the appearance of answers, even for this (what may even be a simple issue to fix although I have searched for hours trying to find solutions), as time is a factor here. I will be monitoring this question throughout the day to provide clarifications in the aid of an answer.
In the first example, I'm not sure what you are writing out as memoryBlock is commented out and never initialized to anything. When you are reading it in, since you are using std::cout to display the data to the console, it MUST be NULL terminated or you will print beyond the end of the memory buffer allocated for memoryBlockTwo.
Either write the terminating null to the file:
memoryBlock = "THEN"; // 4 chars + implicit null terminator
myFile.write(memoryBlock, (sizeof(char)*5));
And/or, ensure the buffer is terminated after it is read:
myFileInput.read(memoryBlockTwo, size);
memoryBlockTwo[size - 1] = '\0';
In your second example, don't do that with C++ objects. You are circumventing necessary constructor calls and if you try that using vectors like you have commented out it certainly won't work like you expect. If the class is plain old data (non-virtual functions, no pointers to other data) you will likely be OK, but it's still really bad practice. When persisting C++ objects, consider looking into overloading the << and >> operators.

Wordlist transfer for an anagram program

I'm almost finished with my program, but there's one last bug that I'm having problems ferreting out. The program is supposed to check about 10 scrambled words against a wordlist to see what the scrambled words are anagrams of. To do this, I alphabetized each word in the wordlist (apple would become aelpp), set that as the key of a map, and made the corresponding entry the original, unalphabetized word.
The program is messing up when it comes to the entries in the map. When the entry is six characters or less, the program tags a random character on the end of the string. I've narrowed down what can be causing the problem to a single loop:
while(myFile){
myFile.getline(str, 30);
int h=0;
for (; str[h] != 0; h++)//setting the initial version of str
{
strInit[h]=str[h]; //strInit is what becomes the entry into the map.
}
strInit[h+1]='\0'; //I didn't know if the for loop would include the null char
cout<<strInit; //Personal error-checking; not necessary for the program
}
And if it's necessary, here's the entire program:
Program
Prevent issues, use normal functions:
getline(str, 30);
strncpy(strInit, str, 30);
Prevent more issues, use standard strings:
std::string strInit, str;
while (std::getline(myFile, str)) {
strInit = str;
// do stuff
}
Best not to use raw C arrays at all! Here's a version, using modern C++:
#include <string>
std::string str;
while (std::getline(myFile, str))
{
// do something useful with str
// Example: mymap[str] = f(str);
std::cout << str; //Personal error-checking; not necessary for the program
}

Initializing array of objects with data from text file

I’m getting system error when I try to compile the code below on Visual C++ 2008 Express. What I’m trying to do is to initialize array of objects with data read from file. I think there is something wrong inside the while loop, because when I initialize these objects manually without the while loop it seems to work. Here is the code and text file:
#include <iostream>
#include <string>
#include "Book.h"
using namespace std;
int main()
{
const int arraySize = 3;
int indexOfArray = 0;
Book bookList[arraySize];
double tempPrice;//temporary stores price
string tempStr;//temporary stores author, title
fstream fileIn( "books.txt" );
while ( !fileIn.eof( ))
{
getline(fileIn,tempStr);
bookList[indexOfArray].setAuthor(tempStr);
getline(fileIn,tempStr);
bookList[indexOfArray].setTitle(tempStr);
fileIn >> tempPrice;
bookList[indexOfArray].setPrice(tempPrice);
if ( indexOfArray < arraySize ) //shifting array index while not exceeding array size
indexOfArray++;
}
fileIn.close();
return 0;
}
and the text file:
Author1
Book1
23.99
Author2
Book2
10.99
Autho3
Book3
14.56
It looks like you are trying to write to bookList[3] in the loop. You will loop through three times filling your array incrementing indexOfArray each time. This will leave indexOfArray at 3 -- your condition as it is written will allow indexOfAray to be incremented to 3. Then if you have a newline after the "14.56" in your data file you will loop one more time and attempt to pass an empty string to bookList[indexOfArray].setAuthor() leading to a segfault since indexOfArray is past the end of the array.
I would suggest ditching the hard-coded array and using a std::vector instead. At the start of each loop just use push_back() to add a new book to the end of the vector and then use back() to access the new element in the array.
There's another run-time error in your code: You don't read an entire line with the call to fileIn >> tempPrice;. The next call to getline() will read to the end of the line, so you'll get an empty string when you're expecting an author.
You're then off by one line in your text file, and you try to convert a title into a double. That make the fstream signal an error, and after that, you're in trouble.
Brett's right, a vector with push_back is a better solution here.
Brett also correctly pointed out that you could run into errors if your file has extra lines. You can fix that by checking if you successfully read from the file:
if(fileIn >> tempPrice)
{
bookList[indexOfArray].setPrice(tempPrice);
}
else
{
break;
}
if(!getline(fileIn,tempStr))
{
break;
}
The key must be in the contents of
#include "Book.h"
I copy-pasted your code, and replaced the #include with my assumption of what class Book might look like:
class Book
{
std::string auth;
std::string title;
double price;
public:
void setAuthor(std::string& str)
{
auth = str;
}
void setTitle(std::string& t)
{
title = t;
}
void setPrice(double d)
{
d = price;
}
};
and it compiled. Perhaps you could share your Book.h, or look there for any problems? Start with some simple definition from Book (like above) and begin readding code until you've found the lines that cause the problem. Its a crude method of figuring out the issue, but sometimes its the most direct way.