Why is string.length()=0 after processing string using string[i]? - c++

I made a program in C++ to take a sentence as input and wanted display the sentence after omitting the spaces , however I'm getting weird results ...
s2 is the string containing the sentence after omitting spaces.
I can access the string s2 as s2[i] , but I'm getting no output when I try cout<< s2; and value of s2.length() gets printed as 0 ??
#include<iostream>
using namespace std;
int main()
{
string s1,s2;
int i,j,l1,l2;
getline(cin,s1);
l1=s1.length();
j=0;
for(i=0;i<l1;i++)
{
if(s1[i]!=' ')
{
s2[j]=s1[i];
j++;
}
}
cout<<s2.length();
cout<<s2<<endl;
}
Expected : s2.length() shouldn't be 0 and cout<< s2; should work.

s2[j]=s1[i];
s2 is initially empty. Accessing s2[j] is out of bounds and undefined behavior.
Change it to s2+=s1[i] and all is good.
Update FYI, in mordern C++ you don't usually need to deal with lengths because you should prefer using standard library algorithms:
#include<iostream>
#include<string>
#include<algorithm>
int main()
{
std::string s1,s2;
getline(std::cin,s1);
std::copy_if(s1.begin(), s1.end(), std::back_inserter(s2), [](char ch){
return ch!=' ';
});
std::cout<<s2.length();
std::cout<<s2<<'\n';
}

The length of a default-constructed string is 0.
s2[j] accesses the character at the index j. If that character doesn't exist, then the behaviour of the program is undefined.
When the length of the string is 0, for any j greater than 0, s2[j] has undefined behaviour, because that character doesn't exist. s2[0] is well defined and refers to the null terminator.
You may have intended to add characters to the string. You can add characters to a string for example using the push_back member function or += operator.

You cannot use [] to increase the s2 string size. If you use resize to grow it beforehand, it should work:
s2.resize(j+1);
s2[j]=s1[i];
j++;
push_back() of a character on a std::string would do the same. However, I offer you this answer as it keeps the character assignment you already had. Philosophy of minimal answer/change :-).

#include<iostream>
using namespace std;
int main()
{
string s1,s2;
int i,l1;
getline(cin,s1);
l1=s1.length();
for(i=0;i<l1;i++)
{
if(s1[i]!=' ')
{
s2.push_back(s1[i]); //CHANGE MADE HERE
}
}
cout<<s2.length();
cout<<s2<<endl;
}
As #iBug mentioned you can also use push_back as shown above

Related

Cannot use string class to assign characters but works with char array in C++

I'm trying to assign some characters from a string to another string as follows:
string s2;
int ind = -1;
for(int i = 0; i < ch; i++)
{
s2[++ind] = s[i];
}
s2[++ind] = '\0';
Now when I try to print s2 using
cout << s2 << endl;
It prints nothing on the black screen. But when I use char array in place of string declaration for s2, then s2 gets printed successfully. I think both a string and char array's characters can be accessed in the same way. Cannot understand why this happened. Some info about this?
You cannot access the ++indd element of an empty string. Use string's operator += to append a char to the string.
Your code contains Undefined Behavior!
At the beginning your string size is zero and you do not resize it. As a result required memory is not allocated and proper information in string is not updated.
Small string optimization probably prevents a crash which should happen.
Two ways to fix it:
// crappy solution - to match your pattern
string s2;
for(int i=0;i<ch;i++)
{
s2.push_back(s[i]);
}
// a good one
string s2(s, s + strlen(s));

How to delete part of a string c++ [duplicate]

I got a string and I want to remove all the punctuations from it. How do I do that? I did some research and found that people use the ispunct() function (I tried that), but I cant seem to get it to work in my code. Anyone got any ideas?
#include <string>
int main() {
string text = "this. is my string. it's here."
if (ispunct(text))
text.erase();
return 0;
}
Using algorithm remove_copy_if :-
string text,result;
std::remove_copy_if(text.begin(), text.end(),
std::back_inserter(result), //Store output
std::ptr_fun<int, int>(&std::ispunct)
);
POW already has a good answer if you need the result as a new string. This answer is how to handle it if you want an in-place update.
The first part of the recipe is std::remove_if, which can remove the punctuation efficiently, packing all the non-punctuation as it goes.
std::remove_if (text.begin (), text.end (), ispunct)
Unfortunately, std::remove_if doesn't shrink the string to the new size. It can't because it has no access to the container itself. Therefore, there's junk characters left in the string after the packed result.
To handle this, std::remove_if returns an iterator that indicates the part of the string that's still needed. This can be used with strings erase method, leading to the following idiom...
text.erase (std::remove_if (text.begin (), text.end (), ispunct), text.end ());
I call this an idiom because it's a common technique that works in many situations. Other types than string provide suitable erase methods, and std::remove (and probably some other algorithm library functions I've forgotten for the moment) take this approach of closing the gaps for items they remove, but leaving the container-resizing to the caller.
#include <string>
#include <iostream>
#include <cctype>
int main() {
std::string text = "this. is my string. it's here.";
for (int i = 0, len = text.size(); i < len; i++)
{
if (ispunct(text[i]))
{
text.erase(i--, 1);
len = text.size();
}
}
std::cout << text;
return 0;
}
Output
this is my string its here
When you delete a character, the size of the string changes. It has to be updated whenever deletion occurs. And, you deleted the current character, so the next character becomes the current character. If you don't decrement the loop counter, the character next to the punctuation character will not be checked.
ispunct takes a char value not a string.
you can do like
for (auto c : string)
if (ispunct(c)) text.erase(text.find_first_of(c));
This will work but it is a slow algorithm.
Pretty good answer by Steve314.
I would like to add a small change :
text.erase (std::remove_if (text.begin (), text.end (), ::ispunct), text.end ());
Adding the :: before the function ispunct takes care of overloading .
The problem here is that ispunct() takes one argument being a character, while you are trying to send a string. You should loop over the elements of the string and erase each character if it is a punctuation like here:
for(size_t i = 0; i<text.length(); ++i)
if(ispunct(text[i]))
text.erase(i--, 1);
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main() {
string str = "this. is my string. it's here.";
transform(str.begin(), str.end(), str.begin(), [](char ch)
{
if( ispunct(ch) )
return '\0';
return ch;
});
}
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s;//string is defined here.
cout << "Please enter a string with punctuation's: " << endl;//Asking for users input
getline(cin, s);//reads in a single string one line at a time
/* ERROR Check: The loop didn't run at first because a semi-colon was placed at the end
of the statement. Remember not to add it for loops. */
for(auto &c : s) //loop checks every character
{
if (ispunct(c)) //to see if its a punctuation
{
c=' '; //if so it replaces it with a blank space.(delete)
}
}
cout << s << endl;
system("pause");
return 0;
}
Another way you could do this would be as follows:
#include <ctype.h> //needed for ispunct()
string onlyLetters(string str){
string retStr = "";
for(int i = 0; i < str.length(); i++){
if(!ispunct(str[i])){
retStr += str[i];
}
}
return retStr;
This ends up creating a new string instead of actually erasing the characters from the old string, but it is a little easier to wrap your head around than using some of the more complex built in functions.
I tried to apply #Steve314's answer but couldn't get it to work until I came across this note here on cppreference.com:
Notes
Like all other functions from <cctype>, the behavior of std::ispunct
is undefined if the argument's value is neither representable as
unsigned char nor equal to EOF. To use these functions safely with
plain chars (or signed chars), the argument should first be converted
to unsigned char.
By studying the example it provides, I am able to make it work like this:
#include <string>
#include <iostream>
#include <cctype>
#include <algorithm>
int main()
{
std::string text = "this. is my string. it's here.";
std::string result;
text.erase(std::remove_if(text.begin(),
text.end(),
[](unsigned char c) { return std::ispunct(c); }),
text.end());
std::cout << text << std::endl;
}
Try to use this one, it will remove all the punctuation on the string in the text file oky.
str.erase(remove_if(str.begin(), str.end(), ::ispunct), str.end());
please reply if helpful
i got it.
size_t found = text.find('.');
text.erase(found, 1);

Substring out of range when converting string to lowercase

I'm attempting to take an input from the command line and then convert it to lower case. To do this, I've written:
istream& operator>>(istream& is, Card& c)
{
static map<string,Card::Rank> mr = createmr();
static map<string,Card::Suit> ms = createms();
string srank, c1, ssuit;
if (is >> srank >> c1 >> ssuit)
{
if (c1 == "of")
{
string nsrank;
string nssuit;
for(unsigned int i = 0; i < srank.length(); i++) {
char temp = srank[i];
nsrank[i] = tolower(srank[i]);
}
It fails on the second iteration of that for loop (more precisely, it fails on nsrank[i] = tolower(srank[i]);). The error that is displayed is "string substring out of range" but I don't understand how this could be the case as there are definitely still characters left in the string.
To give an example:
If I enter "Ace of Spades" then it will iterate through the first time (when i=0) and transfer the 'a' fine. However, it then goes back through with i equaling 1 (which should refer to the 'c') but instead it tells me the substring is out of range (even though the assignment to the char temp works fine). During debugging, "nsrank" claims a size of 15 so I don't see how that could be out of range either....
The problem is that nsrank is an empty string, so accessing with operator[]....
If pos is not greater than the string length, the function never
throws exceptions (no-throw guarantee). Otherwise, it causes undefined
behavior.
This one worked for me: http://ideone.com/3LcYqv
#include <iostream>
using namespace std;
int main() {
string srank="Ace of Spades";
string nsrank;
nsrank.resize(srank.length());
for(unsigned int i = 0; i < srank.length(); i++) {
char temp = srank[i];
nsrank[i] = tolower(srank[i]);
}
cout << nsrank << endl;
return 0;
}
The key is the resize to make nsrank the same size as srank.
Edit: Added compact solution
From many places, among them from this answer
#include <algorithm>
#include <string>
string srank="Ace of Spades";
string nsrank=srank;
std::transform(nsrank.begin(), nsrank.end(),nsrank.begin(), ::toupper);
resize nsrank to match size of srank before entering the loop

C++ string library error: string subscript out of range

string *parse(string str,int from){
int i=0,n=0,j,k;
i=j=from;
string *data=new string[6];
while(str[i]){
if(str[i]==' '){
for(k=0;k<(i-j-1);k++){
data[n][k]=str[j+k]; << Error takes place here
}
data[n][k]='\0';
j=i;
n++;
}
i++;
}
return data;
}
Thanks for your help. I tried to debug but without success, what am I missing?
The problem is that elements data[i] of the data array all have the length of zero. That is why the assignment data[n][k] is always outside of data[n]'s range.
One way of fixing this would be using concatenation:
data[n] += str[j+k];
A better approach would be eliminating the loop altogether, and using substr member function of std::string instead: it lets you cut out a portion of str knowing the desired length and the starting position.
In addition, you are returning a pointer to a local array, which is undefined behavior. You should replace an array with a vector<string>, and add items to it using push_back.
Finally, you need to push the final word when the str does not end in a space.
Here is your modified program that uses the above suggestions:
vector<string> parse(string str,int from){
int i=from, j=from;
vector<string> data;
while(str[i]){
if(str[i]==' '){
data.push_back(str.substr(j, i-j+1));
j=i+1;
}
i++;
}
if (j != str.size()) {
data.push_back(str.substr(j));
}
return data;
}
Here is a demo on ideone.
data starts with 0 length, data[n][k] out of boundry. data[n][k]='\0' is not correct way of using C++ string and string * is considered of bad practice.
To separate a string by space, try:
#include <string>
#include <vector>
#include <sstream>
std::string data("hi hi hi hi hi");
std::stringstream ss(data);
std::string word;
std::vector<std::string> v;
while(std::getline(ss, word, ' '))
{
v.push_back(word);
}

Prog error: for replacing spaces with "%20"

below is the prog i am compiling for replacing spaces with "%20" but when I run it output window shows blank and a message "arrays5.exe has occurred a prob"
#include <iostream>
#include<cstring>
using namespace std;
void method(char str[], int len) //replaces spaces with "%20"
{
int spaces, newlen,i;
for (i=0;i<len;i++)
if(str[i]==' ') spaces++;
newlen=len+spaces*2;
str[newlen]=0;
for (i=len-1;i>=0;i--)
{
if(str[i]==' ')
{
str[newlen-1]='0';
str[newlen-2]='2';
str[newlen-3]='%';
newlen=newlen-3;
}
else
{
str[newlen-1]=str[i];
newlen=newlen-1;
}
}
}
int main()
{
char str[20]="sa h ";
method(str,5);
cout <<str<<endl;
return 0;
}
Please help me finding the error.Thanks
spaces is uninitialised before you increment it.
You should give it an initial, default value.
An uninitialised variable will have a value which is undefined by the specification. This value could be 0, if you're lucky but it is highly likely that this value will be anything in the range of values which the datatype may represent.
Your program will compile and run fine when spaces is initialised properly.
I'm not fixing your problem, but providing a better solution. If you're using C++, then you should use the STL. You've got lots of classes and methods that do all of the job for you.
You could rewrite your 25 lines long method into this 4 lines long method(example included):
#include <iostream>
#include <string>
using namespace std;
std::string method(std::string str)
{
size_t index;
while((index = str.find(' ')) != std::string::npos)
str = str.replace(index, 1, "%20");
return str;
}
int main()
{
std::string str("sa h ");
str = method(str);
cout <<str<<endl; // outputs sa%20h%20
return 0;
}
I would suggest you use std::string, and use the .replace method. The reason your code doesn't work is because you're overwriting the input string in an odd way so I don't know if your expected output would be correct, however, the actual error you have is that you're potentially rewriting at index locations -3, -2, and -1. Consider the case where your first space is at index zero.
In C++, it's usually better to avoid char* unless you have a clear reason for doing so. As a matter of good style (this is somewhat subjective), I would suggest that you do NOT modify your input arguments directly, but instead return the result.
Ie, your method prototype should be:
std::string method(std::string str)
There is no-longer a need to pass the length of the string, because std::string takes care of that.