Spinning words in C++ and recieving error - c++

I'm trying to create a program according to the prompt below but I keep recieving a Caught std::exception, what(): basic_string::at: __n (which is 0) >= this->size() (which is 0) error, I though I was solid at C++ but I guess time takes its toll. My code is down below. Basically, first I parse the string by space character and save them in a vector<string> after that I check if a word is larger than 5 and reverse it if it is and do nothing if it is not. If it isn't the final word, I add a space at the end. Bing bang boom, prompt complete, or at least I thought.
std::string spinWords(const std::string &str)
{
std::vector<std::string> words;
std::string spinnedWord;
int count = 0;
for(unsigned int i = 0; i < str.length(); i++)
{
char currentChar = str.at(i);
if (currentChar == ' ')
{
count++;
continue;
}
else if((int)words.size() == count)
{
words.push_back(&currentChar);
}
else
{
words[count] += currentChar;
}
}
for(unsigned int i = 0; i < words.size(); i++)
{
if(words[i].size() >= 5)
{
for (unsigned int j = words[i].length() - 1; j >= 0; j--)
{
spinnedWord += words[j].at(i);
}
}
if(i + 1 != words.size())
{
spinnedWord += ' ';
}
}
return spinnedWord;
}// spinWords
Write a function that takes in a string of one or more words, and
returns the same string, but with all five or more letter words
reversed (Just like the name of this Kata). Strings passed in will
consist of only letters and spaces. Spaces will be included only when
more than one word is present.
Edit1: I have changed words[j].at(i); to words[i].at(j);
and I have changed words.push_back(&currentChar); to words.push_back(std::string(1, currentChar));
From what I currently understand, when I was pushing back &currentChar, I was causing a undefined behavior. I'll look into how to avoid that in the future. However, the error from before is still present, so the question remains unanswered

for (unsigned int j = words[i].length() - 1; j >= 0; j--)
{
spinnedWord += words[j].at(i);
}
You swapped j an i here. It must be words[i].at(j). Also j probably shouldn't be unsigned here, because the loop condition j >= 0 is always true for unsigned integers.
EDIT: the UB concern for line words.push_back(&currentChar) is valid too. The way to fix it is to construct a string from a char explicitly:
words.push_back(std::string(1, currentChar));

words.push_back(&currentChar);
You're trying to construct a std::string from a pointer to a single character. This compiles because there is a matching constructor, but it takes a C-style string, which your pointer to a single character isn't.

Truly speaking I even do not understand your code. For example what the variable count is doing in the program. Or why you are using an additional container like std::vector when all can be and shall be done with an object of the type std::string because it has all resources to do the task.
The container std::vector is needed only if the assignment is to split a string into words and return the words in an object of the type std::vector<std::string>. But your task is entirely different.
Pay attention to that in general there can be more than one space between words. Even if it is not so in any case you shall use a general approach and not rely on that between words there is only one space.
Your function does not make sense for example when the original string starts from a space character. In this case count will be equal to 1 due to this if statement
if (currentChar == ' ')
{
count++;
continue;
}
but the size of the vector will be equal to 0, So as words.size() is not equal to count then the else statement will be executed
else if((int)words.size() == count)
{
words.push_back(&currentChar);
}
else
{
words[count] += currentChar;
}
that results in undefined behavior.
I can suggest the following solution. In the demonstrative program below I do not use the standard algorithm std::reverse because I think that you have to reverse a word by your own code.
Here you are.
#include <iostream>
#include <string>
#include <utility>
std::string spinWords( const std::string &s, std::string::size_type length = 5 )
{
std::string t( s );
const char *delim = " \t";
for ( std::string::size_type i = 0; i != t.size(); )
{
auto pos = t.find_first_not_of( delim, i );
if ( pos != std::string::npos )
{
i = t.find_first_of( delim, pos );
if ( i == std::string::npos ) i = t.size();
if ( length < i - pos )
{
auto n = i - pos;
for ( std::string::size_type j = 0; j < n / 2; j++ )
{
std::swap( t[pos + j], t[i - j - 1] );
}
}
}
else
{
i = t.size();
}
}
return t;
}
int main()
{
std::string s( "1 12 123 1234 12345 123456 1234567 123456789 1234567890" );
std::cout << s << '\n';
std::cout << spinWords( s ) << '\n';
return 0;
}
The program output is
1 12 123 1234 12345 123456 1234567 123456789 1234567890
1 12 123 1234 12345 654321 7654321 987654321 0987654321

Related

How to deduplicate a string where it only deletes consecutive duplicates

The goal of the program is to take a string like "kcck" and delete the consecutive duplicates. It should first iterate through the string and delete cc leaving kk; then go through again and delete kk; then return "empty" since there are no characters left in the string.
Another example, "aabggtcc" should return "bt".
int i;
int j = i+1;
string deduplicate(string input) {
for(i=0; i<input.length(); ++i) {
while(j <input.length()) {
if(input[i] == input[j]) {
input.erase(i);
input.erase(j);
}
else if (input[i] != input[j]) {
++i; ++j;
}
if(input[i] == '\0') {
cout<<"empty";
}
}
}
return 0;
}
int main () {
cout<<deduplicate("aabg")<<endl;
cout<<deduplicate("ag")<<endl;
cout<<deduplicate("btaabb")<<endl;
return 0;
}
When I run the code it gives me:
libc++abi.dylib: terminating with uncaught exception of type std::out_of_range: basic_string
There are couple of issues with your snippet,
deduplicate function is returning zero(0) all the time
j is initialized in global scope and is never reset for new string
As you erase std::string::length() is calculated on new string hence your index i and j are won't point to same laocation.
Here is the snippet with rectified error,
string deduplicate(string input) {
int i = 0;
int j = 0;
while (i < input.length()) {
j = i + 1;
bool isRepeated = false;
while (j < input.length()) {
if (input[i] == input[j]) {
input.erase(j,1);
--j; //as string length is reduced by 1
isRepeated = true;
}
++j;
}
if (isRepeated) {
input.erase(i,1); //remove first letter as well
--i;//sting length is reduced by one
}
++i;
}
return input;
}
int main() {
std::cout << deduplicate("aabg") << endl;
std::cout << deduplicate("ag") << endl;
std::cout << deduplicate("btaabb") << endl;
return 0;
}
output:
bg
ag
t
which can be even simplified as,
std::string deduplicate(std::string input) {
std::string s ="";
for (auto c : input) //loop through all char
{
int f = 0;
for (auto c1 : input)
{
if (c1 == c)
{
f++; //increment if char is found
}
}
if (f == 1)//append char only if it present ones
s += c;
}
return s;
}
You are decreasing the size of the string whenever you call string.erase() that's why the variable i eventually exceeds the "current" string size input.length() in the while loop, and you get an error std::out_of_range: basic_string when you try to access input[i] in the if and else if conditions of the loop.
Try to manually go through the loop on the string on which you got the error and you will see that i has gone out of bound (i.e. i >= input.length()) in the while loop
With C++11 and on, instead of iterating over each character and making the comparison manually, you can use std::basic_string::find_first_not_of to look forward from a position in the string and find the first character not of the current character. If the position returned by .find_first_not_of is more than 1 from the current position, you can use .erase to erase that number characters. If the return is 1, then just increment your current position and repeat.
To operate on all duplicates characters in the modified string, you simply wrap it all in an outer-loop, keep a copy of the string before before entering the inner-loop to remove duplicate characters, and compare if the modified string is equal to your copy or the .length() is zero for your exit condition.
You can do something similar to the following:
#include <iostream>
#include <string>
int main (void) {
std::string s;
while (getline (std::cin, s)) {
std::string current;
do {
size_t pos = 0;
current = s;
while (pos < s.length()) {
size_t duplicates = s.find_first_not_of (s.at(pos), pos);
if (duplicates != std::string::npos && duplicates > pos + 1)
s.erase(s.begin() + pos, s.begin() + duplicates);
else if (duplicates == std::string::npos &&
(s.end() - s.begin() - pos) > 1)
s.erase(s.begin() + pos, s.end());
else
pos++;
}
} while (current != s && s.length());
std::cout << "'" << s << "'\n";
}
}
Example Use/Output
$ echo "kcck" | ./bin/ddcpp
''
$ echo "aabggtcc" | ./bin/ddcpp
'bt'
$ echo "aabg" | ./bin/ddcpp
'bg'
$ echo "ag" | ./bin/ddcpp
'ag'
$ echo "btaabb" | ./bin/ddcpp
'bt'
There are a number of ways to approach the problem and as long as they are reasonably efficient there isn't any one right/wrong way. If you have a modern C++ compiler, letting some of the built-in container functions handle the work is generally a bit more robust than reinventing it on your own. Look things over and let me know if you have questions.

C++ Palindrome program always giving 0 (false) as an output problem; Where is my code wrong?

The problem is that it always outputs 0 (false) as a result. Probably the problem is in the isPalindrome function, but I cannot figure where exactly. Would be grateful if someone helped.
#include <iostream>
#include <cmath>
#include <string>
using namespace std;
bool isPalindrome(string word)
{
bool result;
for (int i = 0; i <= word.length() - 1; i++)
{
if (word.at(i) == word.length() - 1)
{
result = true;
}
else
{
result = false;
}
return result;
}
}
int main()
{
string word1;
int count;
cout << "How many words do you want to check whether they are palindromes: " << flush;
cin >> count;
for (int i = 0; i < count; i++)
{
cout << "Please enter a word: " << flush;
cin >> word1;
cout << "The word you entered: " << isPalindrome(word1);
}
}
Try this one:
bool isPalindrome(string word)
{
bool result = true;
for (int i = 0; i < word.length() / 2; i++) //it is enough to iterate only the half of the word (since we take both from the front and from the back each time)
{
if (word[i] != word[word.length() - 1 - i]) //we compare left-most with right-most character (each time shifting index by 1 towards the center)
{
result = false;
break;
}
}
return result;
}
In this statement
if (word.at(i) == word.length() - 1)
the right side expression of the comparison operator is never changed and have the type std::string::size_type instead of the type char. You mean
if (word.at(i) == word.at( word.length() - 1 - i ))
However there is no sense to use the member function at. You could us the subscript operator. For example
if ( word[i] == word[word.length() - 1 - i ] )
And the loop should have word.length() / 2 iterations.
Also within the loop you are overwriting the variable result. So you are always returning the last value of the variable. It can be equal to true though a string is not a palindrome.
Also the parameter should be a referenced type. Otherwise a redundant copy of the passed argument is created.
The function can be defined the following way
bool isPalindrome( const std::string &word )
{
std::string::size_type i = 0;
std::string::size_type n = word.length();
while ( i < n / 2 && word[i] == word[n - i - 1] ) i++;
return i == n / 2;
}
Another approach is the following
bool isPalindrome( const std::string &word )
{
return word == std::string( word.rbegin(), word.rend() );
}
Though this approach requires to create a reverse copy of the original string.
The simplest way is to use the standard algorithm std::equal. Here is a demonstrative program
#include <iostream>
#include <string>
#include <iterator>
#include <algorithm>
bool isPalindrome( const std::string &word )
{
return std::equal( std::begin( word ),
std::next( std::begin( word ), word.size() / 2 ),
std::rbegin( word ) );
}
int main()
{
std::cout << isPalindrome( "123454321" ) << '\n';
return 0;
}
I hope this one helps you also (corrected also warnings):
bool isPalindrome(string word)
{
bool result = false;
int lengthWord = (int)word.length();
for (int i = 0; i <= (lengthWord / 2); ++i)
{
if (word.at(i) == word.at(lengthWord - i -1))
{
result = true;
continue;
}
result = false;
}
return result;
}
Two possible problems.
You appear to be comparing a character to a number
if (word.at(i) == word.length() - 1)
shouldn't this be
if (word.at(i) == word.at(word.length() - i)) ?
There are 3 returns within the if statement, so no matter what the outcome it's only going to compare one character before returning to the calling function.
As a point of technique, repeated calls to .length inside the loop, which always returns the same value, wastes time and makes the code more difficult to understand.
You need to return as soon as you find a mismatch. If you are looking for a palindrome you only need to compare the first half of the word with the second half in reverse order. Something like
bool isPalindrome(string word)
{
for (int i = 0, j= word.length() - 1; i<j; i++, j--)
// i starts at the beginning of the string, j at the end.
// Once the i >= j you have reached the middle and are done.
// They step in opposite directions
{
if (word[i] != word[j])
{
return false;
}
}
return true;
}
The loop in the function isPalindrome will only execute once, because the return statement is unconditionally executed in the first iteration of the loop. I am sure that this is not intended.
To determine whether a string is a palindrome, the loop must be executed several times. Only after the last character has been evaluated (in the last iteration of the loop) will it be time to use the return statement, unless you determine beforehand that the string is not a palindrome.
Also, in the function isPalindrome, the following expression is nonsense, as you are comparing the ASCII Code of a letter with the length of the string:
word.at(i) == word.length() - 1
Therefore, I suggest the following code for the function:
bool isPalindrome(string word)
{
for (int i = 0; i < word.length() / 2; i++)
{
if (word.at(i) != word.at( word.length() - i - 1) ) return false;
}
return true;
}
As discussed in the comments under your question. You made some mistakes in the code.
Your function should more or less look like this:
bool isPalindrome(string word) {
bool result = true;
for (int i = 0; i <= word.length() - 1; i++)
{
if (word.at(i) != word.at(word.length() - 1 -i))
{
return false;
}
}
return result;
}

Search a string for all occurrences of a substring in C++

Write a function countMatches that searches the substring in the given string and returns how many times the substring appears in the string.
I've been stuck on this awhile now (6+ hours) and would really appreciate any help I can get. I would really like to understand this better.
int countMatches(string str, string comp)
{
int small = comp.length();
int large = str.length();
int count = 0;
// If string is empty
if (small == 0 || large == 0) {
return -1;
}
// Increment i over string length
for (int i = 0; i < small; i++) {
// Output substring stored in string
for (int j = 0; j < large; j++) {
if (comp.substr(i, small) == str.substr(j, large)) {
count++;
}
}
}
cout << count << endl;
return count;
}
When I call this function from main, with countMatches("Hello", "Hello"); I get the output of 5. Which is completely wrong as it should return 1. I just want to know what I'm doing wrong here so I don't repeat the mistake and actually understand what I am doing.
I figured it out. I did not need a nested for loop because I was only comparing the secondary string to that of the string. It also removed the need to take the substring of the first string. SOOO... For those interested, it should have looked like this:
int countMatches(string str, string comp)
{
int small = comp.length();
int large = str.length();
int count = 0;
// If string is empty
if (small == 0 || large == 0) {
return -1;
}
// Increment i over string length
for (int i = 0; i < large; i++) {
// Output substring stored in string
if (comp == str.substr(i, small)) {
count++;
}
}
cout << count << endl;
return count;
}
The usual approach is to search in place:
std::string::size_type pos = 0;
int count = 0;
for (;;) {
pos = large.find(small, pos);
if (pos == std::string::npos)
break;
++count;
++pos;
}
That can be tweaked if you're not concerned about overlapping matches (i.e., looking for all occurrences of "ll" in the string "llll", the answer could be 3, which the above algorithm will give, or it could be 2, if you don't allow the next match to overlap the first. To do that, just change ++pos to pos += small.size() to resume the search after the entire preceding match.
The problem with your function is that you are checking that:
Hello is substring of Hello
ello is substring of ello
llo is substring of llo
...
of course this matches 5 times in this case.
What you really need is:
For each position i of str
check if the substring of str starting at i and of length = comp.size() is exactly comp.
The following code should do exactly that:
size_t countMatches(const string& str, const string& comp)
{
size_t count = 0;
for (int j = 0; j < str.size()-comp.size()+1; j++)
if (comp == str.substr(j, comp.size()))
count++;
return count;
}

Strings over 15 characters get turned to ε■ε■ε■ε■ε■ε■ε■ε■ε

I have been implementing a factory for a component based game engine recently. I am deserializing objects by reading in from a file what component they need and what to initialize them with. It works except for when I try to read in a property longer than 15 characters. At 15 characters, it reads it in perfectly, anything longer and I get "ε■ε■ε■ε■ε■ε■ε■ε■ε" as output.
I am using std::string to store these lines of text.
Example:
JunkComponent2 test "1234567890123456" test2 "123456789012345"
With this the value of test becomes garbage, while test2 stays perfectly intact.
Any idea's what might be going on?
char line[1024];
while (file.getline(line, 1024))
{
std::vector<std::string> words;
std::string word;
int j = 0;
for (unsigned i = 0; line[i] != '\0' && i < 1024; ++i)
{
if (line[i] == ' ' && j > 0 && line[i - 1] != '\\')
{
words.push_back(word);
j = 0;
word = "";
}
else
{
++j;
word += line[i];
}
}
words.push_back(word);
// std::cout << (*Parts)["JunkComponent"]->GetName() << std::endl;
Component* c = (*Parts)[words[0]]->clone(words);
object->AddComponent(words[0], c);
for (std::list<Member*>::iterator it = members.begin(); it != members.end(); ++it)
{
for (unsigned i = 0; i < words.size(); ++i)
{
if ((*it)->GetName() == words[i])
{
if (words[i + 1][0] == '\"')
{
std::vector<char> chars;
chars.push_back('\"');
chars.push_back('\\');
for (unsigned int n = 0; n < chars.size(); ++n)\
{
words[i + 1].erase(std::remove(words[i + 1].begin(), words[i + 1].end(), chars[n]), words[i + 1].end());
}
Container((*it)->GetMeta(), GET_MEMBER(data.GetData(), (*it)->GetOffset()), (*it)->GetName()).SetValue<std::string>(words[i + 1]);
}
else
{
Container((*it)->GetMeta(), GET_MEMBER(data.GetData(), (*it)->GetOffset()), (*it)->GetName()).SetValue<int>(std::stoi(words[i + i]));
}
++i;
break;
}
}
}
}
GET_MEMBER Macro expands to:
#define GET_MEMBER(P, OFFSET) ((void *)(((char *)(P)) + (OFFSET)))
SetValue Function: (data is a void*)
template <typename T>
void SetValue(T data_)
{
memcpy(data, &data_, sizeof(T));
}
I'll take a stab having just eyed your code. GET_MEMBER is really nasty and I think that's where your problem is. It seems to rely on std::string being convertible to char*, which it is not. Why does your code work with strings shorter than 15? Well that's more than likely because std::string on most popular implementations actually contains a special case for strings where it keeps an internal buffer of length 16 ( last element \0 ) to avoid dynamic memory allocation. When the string is larger than 15 this buffer is uninitialized because it isn't used. The correct way to access the string is by using operator[].

remove commas from string

I created a program in C++ that remove commas (,) from a given integer. i.e. 2,00,00 would return 20000. I am not using any new space. Here is the program I created:
void removeCommas(string& str1, int len)
{
int j = 0;
for (int i = 0; i < len; i++)
{
if (str1[i] == ',')
{
continue;
}
else
{
str1[j] = str1[i];
j++;
}
}
str1[j] = '\0';
}
void main()
{
string str1;
getline(cin, str1);
int i = str1.length();
removeCommas(str1, i);
cout << "the new string " << str1 << endl;
}
Here is the result I get:
Input : 2,000,00
String length =8
Output = 200000 0
Length = 8
My question is that why does it show the length has 8 in output and shows the rest of string when I did put a null character. It should show output as 200000 and length has 6.
Let the standard library do the work for you:
#include <algorithm>
str1.erase(std::remove(str1.begin(), str1.end(), ','), str1.end());
If you don't want to modify the original string, that's easy too:
std::string str2(str1.size(), '0');
str2.erase(std::remove_copy(str1.begin(), str1.end(), str2.begin(), ','), str2.end());
You need to do a resize instead at the end.
Contrary to popular belief an std::string CAN contain binary data including 0s. An std::string 's .size() is not related to the string containing a NULL termination.
std::string s("\0\0", 2);
assert(s.size() == 2);
The answer is probably that std::strings aren't NUL-terminated. Instead of setting the end+1'th character to '\0', you should use str.resize(new_length);.
Edit: Also consider that, if your source string has no commas in it, then your '\0' will be written one past the end of the string (which will probably just happen to work, but is incorrect).
The std::srting does not terminate with \0, you are mixing this with char* in C. So you should use resize.
The solution has already been posted by Fred L.
In a "procedural fashion" (without "algorithm")
your program would look like:
void removeStuff(string& str, char character)
{
size_t pos;
while( (pos=str.find(character)) != string::npos )
str.erase(pos, 1);
}
void main()
{
string str1;
getline(cin, str1);
removeStuff(str1, ',');
cout<<"the new string "<<str1<<endl;
}
then.
Regards
rbo
EDIT / Addendum:
In order to adress some efficiency concerns of readers,
I tried to come up with the fastest solution possible.
Of course, this should kick in on string sizes over
about 10^5 characters with some characters to-be-removed
included:
void fastRemoveStuff(string& str, char character)
{
size_t len = str.length();
char *t, *buffer = new char[len];
const char *p, *q;
t = buffer, p = q = str.data();
while( p=(const char*)memchr(q, character, len-(p-q)) ) {
memcpy(t, q, p-q);
t += p-q, q = p+1;
}
if( q-str.data() != len ) {
size_t tail = len - (q-str.data());
memcpy(t, q, tail);
t += tail;
}
str.assign(buffer, t-buffer);
delete [] buffer;
}
void main()
{
string str1 = "56,4,44,55,5,55"; // should be large, 10^6 is good
// getline(cin, str1);
cout<<"the old string " << str1 << endl;
fastRemoveStuff(str1, ',');
cout<<"the new string " << str1 << endl;
}
My own procedural version:
#include <string>
#include <cassert>
using namespace std;
string Remove( const string & s, char c ) {
string r;
r.reserve( s.size() );
for ( unsigned int i = 0; i < s.size(); i++ ) {
if ( s[i] != c ) {
r += s[i];
}
}
return r;
}
int main() {
assert( Remove( "Foo,Bar,Zod", ',' ) == "FooBarZod" );
}
Here is the program:
void main()
{
int i ;
char n[20] ;
clrscr() ;
printf("Enter a number. ") ;
gets(n) ;
printf("Number without comma is:") ;
for(i=0 ; n[i]!='\0' ; i++)
if(n[i] != ',')
putchar(n[i]) ;
getch();
}
For detailed description you can refer this blog: http://tutorialsschool.com/c-programming/c-programs/remove-comma-from-string.php
The same has been discussed in this post: How to remove commas from a string in C
Well, if youre planing to read from a file using c++. I found a method, while I dont think thats the best method though, but after I came to these forums to search for help before, I think its time to contribute with my effort aswell.
Look, here is the catch, what I'm going to present you is part of the source code of the map editor Im building on right now, that map editor obviously has the purpose to create maps for a 2D RPG game, the same style as the classic Pokemon games for example. But this code was more towards the development of the world map editor.
`int strStartPos = 0;
int strSize = 0;
int arrayPointInfoDepth = 0;
for (int x = 0; x < (m_wMapWidth / (TileSize / 2)); x++) {
for (int y = 0; y < (m_wMapHeight / (TileSize / 2)); y++) {
if (ss >> str) {
for (int strIterator = 0; strIterator < str.length(); strIterator++) {
if (str[strIterator] == ',') {`
Here we need to define the size of the string we want to extract after the previous comma and before the next comma
`strSize = strIterator - strStartPos;`
And here, we do the actual transformation, we give to the vector that is a 3D vector btw the string we want to extract at that moment
`m_wMapPointInfo[x][y][arrayPointInfoDepth] = str.substr(strStartPos, strSize);`
And here, we just define that starting position for the next small piece of the string we want to extract, so the +1 means that after the comma we just passed
strStartPos = strIterator + 1;
Here, well since my vector has only 6 postions that is defined by WorldMapPointInfos we need to increment the third dimension of the array and finally do a check point where if the info has arrived the number 6 then break the loop
arrayPointInfoDepth++;
if (arrayPointInfoDepth == WorldMapPointInfos) {
strStartPos = 0;
arrayPointInfoDepth = 0;
break;
}
}
}
}
}
}
Either way on my code, think abt that the vector is just a string, thats all you need to know, hope this helps though :/
Full view:
int strStartPos = 0;
int strSize = 0;
int arrayPointInfoDepth = 0;
for (int x = 0; x < (m_wMapWidth / (TileSize / 2)); x++) {
for (int y = 0; y < (m_wMapHeight / (TileSize / 2)); y++) {
if (ss >> str) {
for (int strIterator = 0; strIterator < str.length(); strIterator++) {
if (str[strIterator] == ',') {
strSize = strIterator - strStartPos;
m_wMapPointInfo[x][y][arrayPointInfoDepth] = str.substr(strStartPos, strSize);
strStartPos = strIterator + 1;
arrayPointInfoDepth++;
if (arrayPointInfoDepth == WorldMapPointInfos) {
strStartPos = 0;
arrayPointInfoDepth = 0;
break;
}
}
}
}
}
}