I'm pretty new to C++, coming from Java. I'm working on just extracting the first word from a Char array, so I figure that creating a new array to hold all the first word chars and transferring them till the loop runs into a space in the sentence would work. Here is the code:
void execute(){
//start with getting the first word
char first_word[20];
int i = 0;
while (input[i] != ' '){ // input is a char array declared and modified with cin, obtaining the command.
first_word[i] = input[i];
i++;
}
print(first_word + ' ' + 'h' + ' ' + 'h' + 'a');
}
When trying to execute this, I get the error "Stack around the variable 'first_word' was corrupted". Why is this happening?
cin >> input doesn't do what you think it does when input is a character array. The >> operator cannot read into a character array, you have to use cin.getline() or cin.read() instead. So you are actually causing undefined behavior by processing invalid memory.
Even if it did work, you are also not doing any bounds checking. If input has more than 20 characters before encountering a space character (or worse, does not contain a space character at all), you will write past the end of first_word into surrounding memory.
If you really want to code in C++, don't use character arrays for strings. That is how C handles strings. C++ uses std::string instead, which has find() and substr() methods, amongst many others. And std::cin knows how to operate with std::string.
For example:
#include <string>
void execute()
{
//start with getting the first word
std::string first_word;
std::string::size_type i = input.find(' '); // input is a std::string read with cin, obtaining the command.
if (i != std::string::npos)
first_word = input.substr(0, i);
else
first_word = input;
std::cout << first_word << " h ha";
}
Related
First of all, I am nothing but new to both programming and Stack Overflow.
I am self-studying with Schaum's outline for Programming with C++ and I have some issues with problem 8.24 (solutions are given to almost every problem in the book, but I want to know why my code in particular isn't working as expected).
You are supposed to be given a c-string and return the given string, but with all its tokens in reverse order (but keeping the natural order of the token itself).
That is, given "Enter a sentence" it would show on screen "sentence a Enter".
My code is the following:
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char line1[100];
cout << "Enter a sentence (enter \".\" to terminate input):\n";
cin.getline(line1,100,'.');
char line2[strlen(line1) + 1]; // we add 1 for the empty char that ends every c string
int char_count = strlen(line1); //strlen() does not include the empty char
char* p = strtok(line1," ");
while (p)
{
char_count -= strlen(p); // we substract p's len to start adding its chars
for (int i = 0; i <= strlen(p); i++)
line2[char_count + i] = p[i]; // we then add the chars themselves
if ((char_count - 1) > 0)
line2[--char_count] = ' '; // a blanck space is needed between the different tokens
p = strtok(NULL, " ");
}
cout << "\n" << line2 << "\n";
}
Unfortunately, the code is wrong in many ways. The most obvious thing is the obscurity of the word reversal process (and the fact it is mixed with word iteration).
According to the commenters, you are not using C++. In C++ it would be rather straightforward:
#include <algorithm>
#include <iostream>
#include <string>
void reverse_words(std::string& s) {
/* starting position of the word */
size_t last_pos = 0;
do {
/* find the end of current word */
size_t end_pos = std::min( s.find(' ', last_pos + 1), s.size() );
/* reverse one word inplace */
std::reverse(s.begin() + last_pos, s.begin() + end_pos);
/* advance to the begining of the next word */
last_pos = end_pos + 1;
} while (pos != std::string::npos);
std::reverse(s.begin(), s.end());
}
int main()
{
std::string s = "This is a sentence";
reverse_words(s);
std::cout << s << std::endl;
}
Hopefully, you can see the essence of the method: sequentially find start and finish of each word, reverse letter order in this word and then finally reverse the entire string.
Now, getting back to the C-string question. You can replace std::string::find call with strtok and write your version of std::reverse specialized for C strings (the reversal of the entire string or its part is simpler than reversing the word order and this is also the recommended exercise).
Start from a simpler program which prints out pairs of integers (start_pos and end_pos for each word) using strtok. Then write a reverse procedure and test it also. Finally, combine this word iteration with reverse. I personally think this is the only way to be sure your implementation is correct - being sure in each of its parts and being able to test each part individually.
A lot of improvements have been added to C++ since that book was originally written, and we can do it in a lot cleaner and safer way now. We'll break the problem into two parts:
A function to convert a string into a list of tokens
The main function, which reads the string; reverses it; and prints it.
These functions will be tokenize, which returns a vector of string_view, and main. A string_view is just a class that stores a pointer and a size to some other string. It's efficient because it won't make a copy of the string or allocate any memory. In this case, it's the right tool for the job because we're going to be breaking up an existing string.
#include <string_view>
#include <string>
#include <iostream>
#include <vector>
#include <algorithm>
auto tokenize(std::string_view line) {
std::vector<std::string_view> tokens;
for (size_t token_size = line.find(' ');
token_size != line.npos;
token_size = line.find(' '))
{
tokens.push_back(line.substr(0, token_size));
line.remove_prefix(token_size + 1);
}
tokens.push_back(line);
return tokens;
}
int main() {
std::string line;
std::getline(std::cin, line);
auto tokens = tokenize(line);
std::reverse(tokens.begin(), tokens.end());
for(auto token : tokens) {
std::cout << token << ' ';
}
std::cout << std::endl;
}
Explaining tokenize
Tokenize takes a string_view as input, and returns a list of the tokens. line.find(' ') will look for a space. If it finds one, it'll return the position of the space; otherwise, it'll return line.npos (which is basically the biggest possible size).
For every token we find, we
get the token via view.substr(0, token_size)
Add the token to the vector via tokens.push_back
Then, we update the line by removing the first token and the corresponding space. This is line.remove_prefix(token_size + 1);
Once there are no more spaces, we'll add the remainder of the line to the vector using tokenize.push_back(line);, and then we'll return the vector of tokens.
Explaining main
We can get the line via std::getline(std::cin, line);, which will read a line from cin and put it in the variable we give it (line). After that, we can read all the tokens in the line using the tokenize function we wrote. We'll reverse the vector of tokens via std::reverse, and then we'll print out all the tokens.
Thanks to each of you.
Seeing your answers I have learnt quite a lot about good programming (both regarding syntax and original ways to solve the problem itself, as Viktor's).
I apologise if I have not given the proper feedback, but again I am (still) unfamiliar with Stack's customs and ''policies''.
I was wondering if there is a way of using the cin.get() fuction in a loop to read a string that could be composed of more than one word.
For example
while (cin.get(chr)) // This gets stuck asking the user for input
while (cin.get(chr) && chr != '\n') // This code doesn't allow the '\n' to be read inside the loop
I want to be able to read a whole string and be able to use my chr variable to determine if the current character being read is a '\n' character.
I'm a little familiar with the getline function. But I don't know of a way to individually go through every character in the string while counting them when using the getline. I hope what I'm saying makes sense. I'm new to programming and c++.
I basically want to determine when these characters ( ' ' , '\n') happen in my string. I will use this to determine when a word ends and a new one begins in my string.
If you want to read a whole line and count the spaces in it you can use getline.
std::string s;
std::getline(std::cin, s);
//count number of spaces
auto spaces = std::count_if(s.begin(), s.end(), [](char c) {return std::isspace(c);});
std::getline will always read until it encounters an \n.
You could try the following:
using namespace std;
int main() {
char ch;
string str = "";
while (cin.get(ch) && ch != '\n')
str += ch;
cout << str;
}
and the string str would have all characters till end line.
This code here is for reversing words in a string. The problem is that it only reverses the first word in the string. When I ran a trace I found that it is stopping after encountering the statement if(s[indexCount] == '\0') break;
Why the code is getting null character every time the first word is reversed even though some other character is present after the first word.
#include <iostream>
using namespace std;
int main()
{
string s;
char tchar;
int indexCount=0,charCount=0,wordIndex;
cin>>s;
while(1){
if(s[indexCount]==' ' && charCount==0) continue;
if(s[indexCount]==' ' || s[indexCount]=='\0' ){
wordIndex=indexCount-charCount;
charCount=indexCount-1;
while(charCount!=wordIndex && charCount>wordIndex){
tchar=s[wordIndex];
s[wordIndex]=s[charCount];
s[charCount]=tchar;
charCount--;
wordIndex++;
}
if(s[indexCount] == '\0') break;
indexCount++; charCount=0;
}
else{
charCount++;
indexCount++;
}
}
cout<<"\nReveresed words in the string : \n\t"<<s<<endl;
return 0;
}
Also I'm using while(1). Does it make this a bad code?
The problem indeed lies with the method of input. cin >> string_variable will consider whitespace to be a delimiter. That is why only the first word is being entered. Replace cin >> s; with getline(cin, s); and it will work correctly.
First of all I want to point out that
cin >> stringObject;
will never ever read space character! so inserting My name is geeksoul will cause above code to read only My and leave everything else in the buffer!
To read space character you should use getline function like this
std::getline(std::cin, stringObject);
read about getline
Second The standard doesn't say that in case of an std::string '\0' is any special character. Therefore, any compliant implementation of std::string should not treat '\0' as any special character. Unless of course a const char* is passed to a member function of a string, which is assumed to be null-terminated.
If you really want to check your string with null terminating character then you should consider using stringObject.c_str() which converts your C++ style string to old school C style string!
Check this for c_str
Finally this might be helpful for you!
Quick tip.
If you reverse all characters in the whole strings, and then all characters between each pair of consecutive spaces, you will achieve the same result, with way simple code, like this: (Note, this may not compile or be slightly buggy (haven't compiled or anything), but should convey the basic idea)
void reverseWords(std::string& aString) {
std::reverse(aString.begin(), aString.end());
size_t lastSpaceIndex = 0;
for (size_t index = 0; index != aString.size(); ++index) {
if (aString[index] == ' ') {
std::reverse(aString.begin() + lastSpaceIndex + 1, aString.begin() + index);
lastSpaceIndex = index;
}
}
}
I tried to write myself a textcounter which tells me how many characters and words are in a piece of text. Every time I try to paste in a long piece of text for it to count, it will crash or display something random.
Does anyone have any suggestions?
This is what I have written:
#include <iostream>
#include <string>
using namespace std;
int main()
{
cout << "Text counter\nPlease insert text.\n";
string text = "";
getline(cin, text);
double countTotal = text.size();
cout << "Total characters: " << countTotal << "\n";
int wordCount = 1;
for (int chrSearch = 0; chrSearch < (int)text.size(); chrSearch++)
{
char chr = text.at(chrSearch);
if(chr == ' ')
{
wordCount++;
}
}
cout << "Total words: " << wordCount << "\n";
return 0;
}
First of all, the code reads at most one line: std::getline(std::cin, line) stops reading upon receiving the first newline. You can specify a character where to stop, e.g, the character '\0' is unlikely to be present in typical text. For example, you could use:
std::string text;
if (std::getline(std::cin, text, '\0')) {
// do something with the read text
}
You should also always check that input was successful. While the above would work with short texts, when the texts become large it makes more sense to read them one line at a time and eventually reading a line will fail when the end of the stream is reached.
In case you don't like the approach of reading everything up to a null character, you could read the entire stream using code like this:
std::istreambuf_iterator<char> it(std::cin), end;
std::string text(it, end);
if (!text.empty()) {
// do something with the read text
}
A few notes on the other parts of the code:
Don't use double where you mean to use an integer. You may want to use a bigger integer, e.g., unsigned long or unsigned long long but double is for floating point values.
When iterating through a sequence you should either use an unsigned integer type when dealing with indices, e.g., unsigned int or std::size_t. This way there is no need to cast the size(). Preferably you'd use iterators:
for (auto it(text.begin()), end(text.end()); it != end; ++it) {
char chr(*it);
// ...
}
or
for (char chr: text) {
// ...
}
Note that your word count is wrong if there are two consecutive spaces. Also, if you don't break your text using line breaks, you need to use '\n' as an additional whitespace character separating words. If you want to consider all spaces, you should actually use something like this to determine if a character is a space:
if (std::isspace(static_cast<unsigned char>(chr)) { ... }
The static_cast<unsigned char>(chr) is needed because char tends to be signed and using a negative value with std::isspace() results in undefined behavior. Casting the character to unsigned char avoids any problems. Note that negative characters are not entirely uncommon: for example, the second character of my last name (the u-umlaut 'ΓΌ') normally result in a negative char, e.g., when UTF-8 or ISO-Latin-1 encoding is used.
void piglatin(string str)
{
string temp = str; //copies the string passed to the function into temp.
temp = temp + str.at(0); //adds the first character of the word to the end of the word.
temp = temp.erase(0, 1); //deletes the first character from the temp string.
temp = temp + "AY"; //adds "AY" to the temp string
cout << temp << " "; //prints out the word followed by a space.
}
string userIn("I NEED THIS IN PIG LATIN");
istringstream iss(userIn);
do
{
string sub;
iss >> sub;
piglatin(sub);
} while (iss);
so i am trying to use this method to split a string in C++ but I keep on getting an error but the program does what i want it to do. I just need to get rid of the error R6010.
Your code is mostly okay, it's just that you're checking for end-of-file (or end-of-string in this case) wrongly and that's causing an empty string to be sent to piglatin(), resulting in the str.at(0) exception.
You can fix that (including making the code a complete workable program) with something like:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
void piglatin(string str) {
string temp = str;
temp = temp + str.at(0);
temp = temp.erase(0, 1);
temp = temp + "AY";
cout << temp << " ";
}
int main () {
string userIn("I NEED THIS IN PIG LATIN");
istringstream iss(userIn);
string sub;
while (iss >> sub)
piglatin(sub);
cout << '\n';
return 0;
}
The output you then get is:
IAY EEDNAY HISTAY NIAY IGPAY ATINLAY
which I don't think is totally correct Pig Latin (I seem to recall there were slightly different rules where words started with vowels, and you had to move consonant groups to the end rather than just the first one), but I'll leave you to fix that problem if necessary.
In terms of how the loop works:
while (iss >> sub)
piglatin(sub);
This simply continues until the extraction of an item (word in this case) fails. Naive code (which I'll freely admit I've been guilty of) would use something like:
do {
string sub;
iss >> sub;
piglatin(sub);
} while (!iss.eof());
but that doesn't take into account the fact you may not be at the end of the file even when the extraction would fail, such as if your phrase had a space at the end, or if you were scanning integers when the next token in the stream was a non-integer.
And, just as an aside, there's no real need to do all those operations in piglatin() separately, nor is there a need for an (explicit) temporary string. You could replace the whole lot with:
void piglatin (string str) {
cout << str.substr(1) + str.at(0) + "AY ";
}
It might be worthwhile keeping it as separate operations if you ever intend switching to "proper" Pig Latin since it's likely to be more complex, or if this is an assignment and you're not expected to be a C++ expert :-)
... and because of the bug mentioned in the first answer, your code ends up making a call to piglatin() passing an empty string.
At that point, str.at(0) results in obvious undefined behavior (there is no character #0 in a completely empty string), which throws the runtime exception that you're complaining about.