Replacing all spaces in a string with '%20' (C++) - c++

Having some trouble understanding parts of the code; the output I am getting is also wrong. The problem is to replace all spaces in a string with '%20'. The full code is shown below; it compiles but doesn't run exactly as it should.
#include <iostream>
#include <string>
using namespace std;
void replaceSpaces(string str){
//Getting the length of the string, counting the number of spaces
int strLen = str.length();
int i, count = 0;
for (i = 0; i <= strLen; i++) {
if(str[i]==' ')
count++;
}
//Determining the new length needed to allocate for replacement characters '%20'
int newLength = strLen + count * 2;
str[newLength] = '\0';
for (i = strLen - 1; i >= 0; i--) {
if (str[i] == ' ') {
str[newLength - 1] = '0';
str[newLength - 2] = '2';
str[newLength - 3] = '%';
newLength = newLength - 3;
}
else {
str[newLength - 1] = str[i];
newLength = newLength -1;
}
}
cout << str <<endl;
}
int main() {
string str = "hello jellybean hello";
replaceSpaces(str);
return 0;
}
I am probably missing something obvious, but when allocating for the new string length in this line:
int newLength = strLen + count * 2;
Here we are multiplying the number of spaces by 2, but if we are trying to replace all spaces with '%20', why not multiply it by 3?
str[newLength] = '\0';
Does this line indicate that the position past the last character in the string is assigned a null space?
Am also confused about the else statement.
else {
str[newLength - 1] = str[i];
newLength = newLength -1;
}
Not sure if I completely understand the circumstance when this would be executed.
When the functions are compiled and run, if
string str = "hello jellybean hello";
the expected output would be hello%20jellybean%20hello, except the output I am getting is hello%20jellybean%20h.
In terms of time complexity, since there are two independent for loops, would the time complexity be O(n)?
I know I'm asking a lot of different questions, many thanks in advance for any answers!

This is wrong:
str[newLength] = '\0';
std::string objects maintain their NUL terminator internally based on their size. You want
str.resize(newLength);
instead.

int newLength = strLen + count * 2;
says to allocate space (later), equal to the length of the string, plus the number of whitespaces found multiplied by two, which makes sense.
For example: so glad to help, should use the slots that the whitespaces live into for the % and they will need two more slots each, for the 20 part of the replacement that will come into play.
This is WRONG:
str[newLength] = '\0';
can't you see? You access memory out of the bounds of your string. You act like you actually allocated space equal to the newLength, but you haven't that anywhere in the code yet.
Out of bounds accessing result in Undefined Behavior and that's bad.
The else statement is just for copying non-whitespace characters, but you should already given up on that code (if it's not yours) and start from scratch or/and take a sneak peak at: Encode/Decode URLs in C++.
As for the wrong result, you should know by reaching that point of that answer, that this is expected.

Trying to do the modification in place is tricky. It's much easier to create a new string:
std::string new_string;
for (int i = 0; i < str.length(); ++i) {
if (str[i] == ' ')
new_string += "%20";
else
new_string += str[i];
}
return new_string;
or, if you like range-for:
std::string new_string;
for (char ch : str) {
if (ch == ' ')
new_string += "%20";
else
new_string += ch;
}
return new_string;

You can change that string argument in function to reference, then there wont be any need for new string, at other part of the code, you can use insert function to add '2' and '0', and you only need to convert space to '&'.
void replaceSpaces(string &str) {
size_t strLen = str.length();
for (int i = 0; i < strLen; i++) {
if (str[i] == ' ') {
str[i] = '%';
str.insert(str.begin() + i + 1, '2');
str.insert(str.begin() + i + 2, '0');
strLen += 2;
}
}
}

This is easy; replace examplestring with your string in the code, and use as you would:
#include <iostream> //debug output
#include <string>
using std::string;
using std::cout;
using std::endl;
//the string to convert
string examplestring = "this is the example string for spaces into %20";
int main()
{
int countspaces = 0; //its faster to fill a known size
for (auto &x : examplestring)if (x == ' ')countspaces++; //counts spaces
string newstring; //declare new string
newstring.resize(examplestring.size() + (countspaces*3)); //pre-set size to make it run faster
int newstringiterator = 0; //keep track of new string location
//if ' '(space), place %20 in newstring and add 3 to iteration
//else just place the letter and iterate
for (int i=0;i<examplestring.size();i++)
{
if (examplestring[i] == ' ')
{
newstring.insert(newstringiterator, "%20");
newstringiterator += 3;
}
else newstring[newstringiterator++] = examplestring[i];
}
//final newstring is the original with %20 instead of spaces.
cout << newstring << endl;
system("PAUSE"); //to read console output
return 0; //return to zero
}
This will output newstring, which is the old string with '%20' instead of spaces.

Related

C++ insert symbol if rotation is specific character

I'm trying my luck at decrypting/crypting and I want to insert characters if a said rotation would result in a few specific characters. I have a constant string called CHARS ="ABCXYZabcxyz". My crypted string at the moment is "eDhrS3S0/".
I am using ASCII rotation 4, and if the current string character would be one of my characters from "CHAR" I want to add / before and / after the said character, but I cant get it working, this is my code at the moment for this.
const string CHARS="ABCXYZabcxyz";
string crypt = "eDhrS3S0/", encrypted;
string cryptTemp = crypt;
for (int i=0; i<cryptTemp.length(); i++){
for (int j=0; j<CHARS.length(); j++){
if (((int)crypt[i]-4) == (int)CHARS[j]){
crypt.insert(crypt[i],"0",-1);
crypt.insert(crypt[i],CHARS[j], 0);
crypt.insert(crypt[i],"0",+1);
}
}
}
I manage to replace the characters if they match chars without rotation, but once I add "-5" in the if statement nothing happens and I am really stuck at this point. The first character in the string "e" should translate to "a" after I remove 4 from it, but I cant get it working.
Adding some separation of concerns will make your code clearer:
Pull out the rot4 code into a separate function.
Explicitly call this function and assign the result to c
Use std::string#find instead of a loop.
Accumulate all characters in ret and return that.
char rot4(char c) {
bool wasupper = isupper(c);
c = tolower(c);
int value = int(c - 'a') - 4;
if (value < 0) value += 26;
c = value + (wasupper ? 'A' : 'a');
return c;
}
string decrypt(string crypt) {
string ret;
for (int i=0; i<crypt.length(); i++){
char c = rot4(crypt[i]);
if (CHARS.find(c) != string::npos) {
ret += '/';
ret += c;
ret += '/';
} else {
ret += c;
}
}
return ret;
}
As for your original question, I'm pretty sure you were using the wrong overload of std::string#insert.

Trimming start of Cstring without copying

I managed to get my homework to work but It shouldn't work because i have not finished it. I don't know why it does. I need help.
#include<iostream>
using namespace std;
char* trim(char* str) {
const int lenStr = strlen(str);
int characters = 0;
bool trimmableFront = false;
int firstChar;
//check if trimmableFront + location of first char
for (int i = 0; i < lenStr; i++) {
if (*(str + i) != ' ') {
if (characters == 0)
firstChar = i;
characters++;
}
if (characters == 0) {
trimmableFront = true;
}
}
//trim Front //THIS PART SHOULD BEHAVE DIFFERENTLY
if (trimmableFront) {
for (int i = 0; i < lenStr; i++) {
if((firstChar + i <= lenStr))
*(str + i) = *(str + firstChar + i);
}
}
return str;
}
int main() {
char str[] = " why does it work?";
trim(str);
cout<< str <<endl;
return 0;
}
At the end of trim(*char) function, trimmed string should have still leftovers from previous locations.
For some reason it is perfectly trimmed and works as intended printing "why does it work?" but it should print something like "why does it workt work?"
The reason why it works is because as you trim the string by shifting each character you also shift the terminating null character '\0'. As you probably know c-strings are array of characters terminated by '\0', so as you print str with cout all characters are printed until the null value is reached: that is way the leftovers are not printed.

Run-Time Check Failure #2 - S, Visual Studio C++

Here is method that replaces all spaces in a string with '%20'. It works fine in general, but complains when execution finishes with 'Run-Time Check Failure #2 - S'. Is there an issue with my for loop?
void replaceSpace(char *s) {
int spaces = 0;
for (int i = 0; i < strlen(s); i++) {
if (s[i] == ' ') {
spaces++;
}
}
// new string that includes overwriting space, and two additional chars
int newLen = strlen(s) + spaces * 2;
s[newLen] = '\0';
for (int i = strlen(s) - 1; i >= 0; i--) {
if (s[i] == ' ') {
s[newLen - 1] = '0';
s[newLen - 2] = '2';
s[newLen - 3] = '%';
newLen -= 3;
}
else {
s[newLen - 1] = s[i];
--newLen;
}
}
}
char test[] = "rep lace Spac e";
replaceSpace(test);
cout << test << endl; //rep%20lace%20Spac%20e
edit: I ran this through cpp shell and did not have any issues oddly. Ill try updating visual studio 2015, and report back.
edit2: Nope, same error.
When you define test
char test[] = "rep lace Spac e";
you define an array of exactly 16 characters (don't forget the string terminator). There is no way to expand the array, meaning you will write out of bounds of the array which leads to undefined behavior.
The solution, of course, is to use std::string instead, and append to it.
Really, this code is not necessary. Check it:
#include <iostream>
#include <string>
#include <algorithm>
#include <cctype>
int main()
{
std::string s("rep lace Spac e");
s.erase(std::remove_if(s.begin(), s.end(), static_cast<int(*)(int)>(std::isspace)), s.end());
std::cout << s;
}

Trying to reverse a string in C++, but gibberish appears at the of the reversed string?

Here is my function to reverse a string:
void reverse(char *str){
int lengthStr = strlen(str);
int j, i;
char reversedString[100];
for (j = 0, i = lengthStr-1; i >= 0; i--, j++){
reversedString[j] = str[i];
}
cout << reversedString;
}
The string does appear as reversed, but at the end there's a bunch of weird characters that appears. What could be causing this issue?
If you want to reverse the string there are many cleaner approaches already available like: std::reverse_copy etc.
But if you want to fix this function then try this:
char reversedString[100];
memset(reversedString, 0, 100*sizeof(char);
OR
for (j = 0, i = lengthStr-1; i >= 0; i--, j++){
reversedString[j] = str[i];
}
reversedString[j] = '\0'; //! Add null character at the end to indicate end of the string
cout << reversedString;
Note: Your program fails if input string has length >= 100.
Add a zero to the characters you assigned to reversedString. Otherwise, the unitialized extra portion of the new string will show up as garbage. A zero is used to mark the end of the string.
First of all the function is invalid and has no sense. You do not reverse the original string and the local string that is defined in the function can be less than the original string. Also you do not append the reversed local string with the terminating zero. The function can look like
void reverse( char *str )
{
size_t n = std::strlen( str );
for ( size_t i = 0; i < n / 2; i++ )
{
char c = str[i];
str[i] = str[n - i - 1];
str[n - i - 1] = c;
}
}
The original reversed string can be displayed in the code that calls the function.

Shifting a string of characters?

I'm writing a program that solves Caesar ciphers in C++. It takes a string of the alphabet and shifts it to the left each loop: "abc....yz" --> "bcd.....yza". The problem is after another loop it goes: "bcd.....yza" --> "cde.....yzaa".
char temp; // holds the first character of string
string letters = "abcdefghijklmnopqrstuvwxyz";
while (true)
{
temp = letters[0];
for (int i = 0; i < 26; i++)
{
if (i == 25)
{
letters += temp;
}
letters[i] = letters[i + 1];
cout << letters[i];
}
cin.get();
}
Copy and paste that code and you'll see what I'm talking about. How do I fix this mysterious problem?
If I'm not mistaken, your loop does precisely the same as the following code:
letters = letters.substr(1,25) + letters.substr(0,1);
// [skip 1, take 25] + [first char goes last]
I think you need letters to be 27 characters, not 26, and instead of letters += temp (which grows the string every time), use letters[26] = temp[0].
...at which point you can just ditch temp entirely:
string letters = "abcdefghijklmnopqrstuvwxyz.";
while (true)
{
letters[26] = letters[0];
for (int i = 0; i < 26; i++)
{
letters[i] = letters[i + 1];
cout << letters[i];
}
cin.get();
}
[edit]
Although the more natural way to handle this is to use arithmetic on the characters themselves. The expression 'a' + ((c - 'a' + n) % 26) will shift a char c by n places Caesar-style.
You can achieve this easily using valarray< char >::cshift(n) (cyclical shift) method.