Why does the for loop keep stopping at else statement? - c++

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
main()
{
string str1;
char strArray[80];
cout << "Enter string: ";
getline(cin, str1);
transform(str1.begin(), str1.end(), str1.begin(), ::tolower);
for(int i = 0;i < str1.length();i++)
{
if(str1[i] == ' ' || str1[i] == ',' || str1[i] == '.')
{
}
else
{
strArray[i] = str1[i];
}
}
cout << strArray;
return 0;
}
The for loop keeps stopping after it finds a space, comma, or period. Could someone explain to me why this is happening?

The problem is that i keeps incrementing even though you erased a character from the input. It's not actually stopping, just skipping a character. Since strArray now has a hole in it, it's likely that the hole is filled with 0 thus ending the C-string. P.S. this behavior is not guaranteed and you might end up with completely different results on another run of the program.

When you call erase it affects the container, so you need to handle this. erase() returns the iterator to the next element after the deleted one, so you should use that instead:
int i = 0;
for(string::iterator it = str1.begin(); it != str1.end(); )
{
if(*it == ' ' || *it == ',' || *it == '.')
{
it = str1.erase(it);
}
else
{
strArray[i++] = *it++;
}
}
strArray[i] = '\0'; // terminate string

Can you post which string you input for str1? Cause I try run it and it run well without stop. The only proplem I found with your code is that you erase the char in the loop which will lead to wrong result string.

You are erasing characters from the string while still continuing to increment the counter. Remove ++i from the for loop. Put it under the else clause.
for(int i = 0;i < str1.length();)
{
if(str1[i] == ' ' || str1[i] == ',' || str1[i] == '.')
{
str1.erase(i, 1);
}
else
{
strArray[i] = str1[i];
++i;
}
}
strArray[str1.length()] = '\0';

Related

How to get the number of words from a line from a text file

I am trying to get the number of words from a line in a text file. I used .getline() in order to extract a line from the entire text file. The code is:
#include <iostream>
#include <iomanip>
#include <fstream>
int main()
{
char const* filename = "duck.txt";
std::ifstream ifs{ filename };
constexpr size_t MAX_LINE_LEN{ 2048 };
char line[MAX_LINE_LEN];
int lineCount = 0;
int totalWordCount = 0;
int totalByteCount = 0;
while (ifs.getline(line, MAX_LINE_LEN-1))
{
int lineWord = 0;
char* q = &line[0];
if (ifs.eof())
{
lineCount--;
totalByteCount--;
}
while (*q != '\0')
{
q++;
totalByteCount++;
}
totalByteCount++;
if (*q == '\0')
{
lineCount++;
}
int i = 0;
int j = 0;
while (line[i] != '\0' && line[j] != '\0')
{
while (line[i] == ' ')
{
i++;
}
j = i;
while (line[j] != ' ')
{
j++;
}
lineWord++;
j = i;
}
totalWordCount += lineWord;
}
std::cout << "Total Lines: " << lineCount << '\n' << "Total Words: " << totalWordCount << '\n' << "Total Bytes: " << totalByteCount;
}
But the only important part is:
int i = 0;
int j = 0;
while (line[i] != '\0' && line[j] != '\0')
{
while (line[i] == ' ')
{
i++;
}
j = i;
while (line[j] != ' ')
{
j++;
}
lineWord++;
j = i;
}
totalWordCount += lineWord;
I'm trying to read the line character by character until I reach a non-whitespace character after which I'll assign that subscript to i. Then, I'll set j to the subscript of the first whitespace encountered after the character of line[i]. If j finds a whitespace, then there is a word. If j reaches '\0', then the line has ended and I end the while loop. When I try to compile and run this, the compiler just displays nothing. What am I doing wrong? Also, I can't add anymore header files
In your original code:
int i = 0;
int j = 0;
while (line[i] != '\0' && line[j] != '\0')
{
while (line[i] == ' ') // you do not check for end of string character?
{
i++;
}
j = i; // here i = j = beginning of word.
// since you rewind to beginning of the word below,
// your program keeps repeating this loop endlessly
while (line[j] != ' ') // this loop could easily run for quite a while.
// until it seg-faults
{
j++;
}
lineWord++;
j = i; // BUG Here! you're 'rewinding' j to to beginning of the word.
// you loop back and keep counting the same word over and over.
}
totalWordCount += lineWord;
What is the purpose of i and j ? Wouldn't the code be simpler and easier to read and maintain using a single pointer? Or a single index? This kind of algorithm is where a pointer would excel, though, as the only arithmetic pointer operation needed is increment.
As in:
const char* p = &line[0];
int word_count = 0;
line[MAX_LINE_LEN - 1] = 0; // making sure the code below stays within boundaries.
for(;;)
{
// skip to next word
while (*p && *p == ' ') ++p; // stay within the string by testing for zero.
if (!*p)
break; // done!
// since p now points to the beginning of a word, we've got one
++word_count;
// skip to end of word
while (*p && *p != ' ') ++p;
}
This is all fine, for most cases but there could be some exceptional typos in the text, like "hello, world !", where the last punctuation would be counted as a word. There is also the problem of horizontal tabs, which could also be counted erroneously as words.
To cover these cases, you should test for valid characters for words, instead of for space, which is a rather vague concept.
Without using library calls, You'd need to define what constitutes spaces and punctuation, either with a constant, or with a function.
Substituting the test for space with a more targeted test, using isalnum() to check for alpha or numeric characters:
for(;;)
{
// skip to next word
while (*p && !std::isalnum(*p & 0xFF)) ++p;
if (!*p)
break; // done!
++word_count;
// skip to end of word
while (*p && std::isalnum(*p & 0xFF)) ++p;
}
Beware of function of the isalnum(), isalpha().. family, they define their input as an int, the mask ensures that characters in the 128-255 range are not sign-extended aand are passed correctly as positive values.

What is wrong with my program to find the longest word in a sentence?

#include <iostream>
using namespace std;
int main() {
char a[101]{0};
cin>>a;
cin.getline(a,101);
cin.ignore();
int currLen{0};
int maxLen{0};
int startInd{-1};
int endInd{-1};
for(int i=0; i<101; i++) {
if(a[i]!=' ' ) {
++currLen;
} else if(a[i]==' '||a[i]=='\0') {
if(currLen>maxLen) {
maxLen=currLen;
startInd=i-currLen;
endInd=i-1;
}
if(a[i]=='\0')
break;
currLen=0;
}
}
cout<<maxLen<<endl;
if(startInd==-1)
cout<<-1;
else
for(int i=startInd; i<=endInd; i++)
cout<<a[i];
return 0;
}
If I take an input here, for example, "My name is Manav Kampani"
It will output 5
Manav instead of 7
Kampani
But if I write "My name is Manav Kampani ", with space after the last word
than it is considering Kampani too printing Kampani.
Also when I input "Kampani Manav is my name" then too it's displaying the wrong output. That means it is not considering the first word of the sentence.
if(a[i]!=' ' )
{
++currLen;
}
else if(a[i]==' '||a[i]=='\0')
{
....
}
Consider the case of a[i] == 0. Which of these if-statements will apply.
Answer: the first one. Which means you'll never look at the final word in the string. You also don't exit at the end of the string, but instead loop through whatever is in your string all the way out to character 101.
As a general structure, be very, very careful with this:
if (condition)
else if (condition)
// without a final else section
If you do that, you need to think about what you're doing. In this particular case, you can have:
if (a[i] != 0 && a[i] != ' ')
else
It may not solve all your issues, but it should solve some.
A nice sliding window pattern implementation.
You have 3 problems in your code
You must not write cin >> a;
You must not write cin.ignore();
You need to modify your if statement like so: if (a[i] != ' ' && a[i] != '\0') Otherwise you will not detect the last word.
Your complete working code with that minor fixes will lokk like that.
int main()
{
char a[101]{ 0 };
//cin >> a;
cin.getline(a, 101);
//cin.ignore();
int currLen{ 0 };
int maxLen{ 0 };
int startInd{ -1 };
int endInd{ -1 };
for (int i = 0; i < 101; i++)
{
if (a[i] != ' ' && a[i] != '\0')// Add comparison
{
++currLen;
}
else if (a[i] == ' ' || a[i] == '\0')
{
if (currLen > maxLen)
{
maxLen = currLen;
startInd = i - currLen;
endInd = i - 1;
}
if (a[i] == '\0')
break;
currLen = 0;
}
}
cout << maxLen << endl;
if (startInd == -1)
cout << -1;
else
for (int i = startInd; i <= endInd; i++)
cout << a[i];
return 0;
}
Additionally. You should not use C-Style arrays in C++. And please use std::string
There is a couple of things here:
1- You don't need to do a cin>>a this is actually consuming the first word, and afterwards the content is overrided by cin.getline(). So removing the firsst cin>>ayou'll be fine.
2- The last word is not read because there isn't any if condition that matches the condition aka.
if(a[i]!=' ' ) case of not a space
//not end of word
else if(a[i]==' '||a[i]=='\0') case of space or null
//end of word
So your last character is not a space nor null, that means you don't detect the last word.

removing vowels from c++ string

char arr[5000];
ifstream is("test.txt");
is.get(arr,5000);
int i = 0;
int j = 0;
cout << arr << endl;
char anar[5000];
while (arr[i] != '\0')
{
if (arr[i] == 'i' || arr[i] == 'a' || arr[i] == 'e' ||
arr[i] == 'o' || arr[i] == 'u')
{
++i;
}
else anar[j] = arr[i]; ++j; ++i;
}++j; anar[j] = '\0';
cout << anar << endl;
ofstream os("test.txt");
os.write(anar, sizeof(char));
cout << "written successfully" << endl;
should read the data from a file and delete the vowels from this string. After deleting vowels, it should assign the result to another string. But vowels seem strange characters and the writen file is only one character long.
How big do you think sizeof(char) is? So how many characters is this going to write?
os.write(anar, sizeof(char));
You actually have j characters in your array, so this works
os.write(anar, j);
But since you have a null terminated character array even simpler would be
os << anar;
Some other errors, look at this loop
while (arr[i] != '\0')
{
if (arr[i] == 'i' || arr[i] == 'a' || arr[i] == 'e' ||
arr[i] == 'o' || arr[i] == 'u')
{
++i;
}
else anar[j] = arr[i]; ++j; ++i;
}++j; anar[j] = '\0';
It looks like you are missing {} around the else part of the if statement. You also have an extra ++j after the while loop for some reason. Here's how it should look (I think)
while (arr[i] != '\0')
{
if (arr[i] == 'i' || arr[i] == 'a' || arr[i] == 'e' ||
arr[i] == 'o' || arr[i] == 'u')
{
++i;
}
else
{
anar[j] = arr[i];
++j;
++i;
}
}
anar[j] = '\0';
Notice how much easier these problems are to spot if you get into the habit of consistently indenting your code. You should do this.
BTW there are no C++ strings in your code, only character arrays.
A very good answer has been given by john already. So, the problem is solved.
I would like to recommend to you to learn a little bit about C++ and all the existing libraries. Especially the C++ - algorithms library is very powerful.
Look at the below program:
#include <iostream>
#include <fstream>
#include <algorithm>
#include <iterator>
int main() {
// Open files and check, if they could be opened
if (std::ifstream is("withvowels.txt"); is)
if (std::ofstream os("withoutvowels.txt"); os)
// Copy file and remove vowels
std::copy_if(std::istreambuf_iterator<char>(is), {}, std::ostreambuf_iterator<char>(os), [](const char c) { return !((0x208222 >> (c & 0x1f)) & 1); });
}
So, in essence, we have just 3 statements: 2 times if with initializer. And then one copy_if with a lambda for vowel detection.
If you want to know more about the lambda and vowel detection you can read in one of my other posts here.
EDIT
Op asked, how to read the file into a std::string. I added a new piece of code, where I first read the complete file into a std::string and then erase/remove the vowels. The result is shown on std::cout
Please see:
#include <iostream>
#include <fstream>
#include <algorithm>
#include <iterator>
#include <string>
int main() {
// Open file and check, if it could be opened
if (std::ifstream is("r:\\withvowels.txt"); is) {
// Read the complete file into string variable s
std::string s(std::istreambuf_iterator<char>(is), {});
// Remove all vowels from string
s.erase(std::remove_if(s.begin(), s.end(), [](const char c) { return ((0x208222 >> (c & 0x1f)) & 1); }), s.end());
// Show result
std::cout << s;
}
}

c++ getting weird crashes in my program [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 5 years ago.
Improve this question
#include <iostream>
using namespace std;
int main()
{
int a = 0, skrb = 0, j = 0;
char b, simboliai[2000];
char zodis[50][20];
char check[1][20] = {'f'};
cout << "Prasome irasykite sakini: ";
cin.getline(simboliai,sizeof(simboliai));
//----------------- Zodziu skaidymas ----------------------------------------------------------------------------------------------------------
a = 0;
for (int i = 0; i > -1; i++)
{
if ((simboliai[i] == 's' && simboliai[i++] == ' ') || (simboliai[i] == 's' && simboliai[i++] == '\n'))
{
check[0][a] = 't';
}
if (simboliai[i] == ' ')
{
a++;
}
else
{
zodis[i][a] = simboliai[i];
}
if (simboliai[i] == '\n')
{
break;
}
}
a = 0;
while (1)
{
if (simboliai[a] == '.' || simboliai[a] == ',' || simboliai[a] == '!' || simboliai[a] == '?')
{
skrb++;
}
a++;
if (simboliai[a] == '\n')
{
break;
}
}
a = 0;
cout << "Jus ivedete tokius zodius kurie baigiasi raide 's'" << endl;
while(1)
{
if (zodis[j][a] == 'Ì')
{
cout << '\n';
a++;
}
if (check[0][a] == 't')
{
cout << zodis[j][a];
}
if (zodis[0][a] == 'Ì')
{
break;
}
}
cout << "Pas jus yra (.','!'?) simboliu: " << skrb << endl;
cin.ignore();
cin.get();
}
Basically this program would work but that for part just ruins everything. It doesn't put characters one by one. And when I debug it shows that program have put symbol in its place but then there is Ì.
So it looks just like so
input: word word
zodis[0][0] goes like wÌÌÌÌÌÌÌÌÌÌ zodis [1][0] goes oÌÌÌÌÌÌÌÌÌÌ and so on and it breaks. Thank you in advance.
If you are saying the for loop wouldn't put characters 1 by 1, it was in your "simboliai[i++] == ' '" logic. integer 'i' been incremented twice each loop when current character is 's' which mean it will be incremented from i=2 to i=4 if simboliai[i] = 's'. Use i+1 instead for your checking.
for (int i = 0; i > -1; i++)
{
...
}
is the main problem. There might be others but I don't look too closely for them.
The values of i will be 0, 1, 2. etc.
All of them are greater than -1.
The loop will go on until the value i reaches INT_MAX. (Not sure what happens when i is incremented at that time).
That is way larger than the size of the array simboliai anyway. Your program will access the array simboliai beyond valid limits and cause undefined behavior.
I think what you need is:
size_t len = strlen(simboliai);
for (size_t i = 0; i < len; i++)
{
...
}
Other Problems
Some of the errors result from the assumption that there is a newline character in simboliai. That assumption is not correct. std::istream::getline reads and discards the newline character.
If Google Translate is correct, zodis is supposed to contain a list of words. When you are iterating over the characters of simboliai, you need three counters.
One to iterate over the characters of simboliai.
One to keep track of the number words.
One to keep track of the number of characters in the current word.
Your for loop is not doing that.
When you are trying to access the contents of an array, you need to always write defensive code and make sure that you never access the array using out of bounds indices. In the last while loop, you are not doing that.
In the last while loop, you are incrementing a only in the first if block. If the conditional of that if statement evaluates to false, a never gets incremented and you get stuck in an infinite loop.
In the last loop you use j as an index but its value is initialized to 0 at the start of the function and never gets updated. It's not clear what the intent of the last while loop is. Hence, I can't say whether it's a bug but it sounds like it could be.
Here's a cleaned up version of your posted code with still a few unknowns.
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
int a = 0, skrb = 0, j = 0;
char b, simboliai[2000];
char zodis[50][20];
char check[1][20] = {'f'};
cout << "Prasome irasykite sakini: ";
cin.getline(simboliai,sizeof(simboliai));
//----------------- Zodziu skaidymas ----------------------------------------------------------------------------------------------------------
int word_counter = 0;
a = 0;
size_t len = std::strlen(simboliai);
for (size_t i = 0; i < len; i++)
{
if ((simboliai[i] == 's' && simboliai[i+1] == ' ') || (simboliai[i] == 's' && simboliai[i+1] == '\0'))
{
check[0][a] = 't';
}
if (simboliai[i] == ' ')
{
zodis[word_counter][a] = '\0';
a = 0;
++word_counter;
}
else
{
zodis[word_counter][a] = simboliai[i];
++a;
}
}
a = 0;
while ( simboliai[a] != '\0' )
{
if (simboliai[a] == '.' || simboliai[a] == ',' || simboliai[a] == '!' || simboliai[a] == '?')
{
skrb++;
}
a++;
}
a = 0;
cout << "Jus ivedete tokius zodius kurie baigiasi raide 's'" << endl;
while( j < 50 && a < 20 )
{
if (zodis[j][a] == 'Ì')
{
cout << '\n';
}
if (check[0][a] == 't')
{
cout << zodis[j][a];
}
if (zodis[0][a] == 'Ì')
{
break;
}
a++;
}
cout << "Pas jus yra (.','!'?) simboliu: " << skrb << endl;
cin.ignore();
cin.get();
}

Remove multiple spaces [duplicate]

This is an interview question
Looking for best optimal solution to trim multiple spaces from a string. This operation should be in-place operation.
input = "I Like StackOverflow a lot"
output = "I Like StackOverflow a lot"
String functions are not allowed, as this is an interview question. Looking for an algorithmic solution of the problem.
Does using <algorithm> qualify as "algorithmic solution"?
#include <iostream>
#include <string>
#include <algorithm>
#include <iterator>
struct BothAre
{
char c;
BothAre(char r) : c(r) {}
bool operator()(char l, char r) const
{
return r == c && l == c;
}
};
int main()
{
std::string str = "I Like StackOverflow a lot";
std::string::iterator i = unique(str.begin(), str.end(), BothAre(' '));
std::copy(str.begin(), i, std::ostream_iterator<char>(std::cout, ""));
std::cout << '\n';
}
test run: https://ideone.com/ITqxB
A c++0x - solution using a lambda instead of a regular function object. Compare to Cubbi's solution.
#include <string>
#include <algorithm>
int main()
{
std::string str = "I Like StackOverflow a lot";
str.erase(std::unique(str.begin(), str.end(),
[](char a, char b) { return a == ' ' && b == ' '; } ), str.end() );
}
Keep two indices: The next available spot to put a letter in (say, i), and the current index you're examining (say, j).
Just loop over all the characters with j, and whenever you see a letter, copy it to index i, then increment i. If you see a space that was not preceded by a space, also copy the space.
I think this would work in-place...
I'd just go with this:
int main(int argc, char* argv[])
{
char *f, *b, arr[] = " This is a test. ";
f = b = arr;
if (f) do
{
while(*f == ' ' && *(f+1) == ' ') f++;
} while (*b++ = *f++);
printf("%s", arr);
return 0;
}
I'd propose a little state machine (just a simple switch statement). Because if the interviewer is anything like me, the first enhancement they'll ask you to do is to fully trim any leading or trailing spaces, so that:
" leading and trailing "
gets transformed to:
"leading and trailing"
instead of:
" leading and trailing "
This is a really simple modification to a state-machine design, and to me it seems easier to understand the state-machine logic in general over a 'straight-forward' coded loop, even if it takes a few more lines of code than a straight-forward loop.
And if you argue that the modifications to the straight forward loop wouldn't be too bad (which can be reasonably argued), then I (as the interviewer) would throw in that I also want leading zeros from numbers to be be trimmed.
On the other hand, a lot of interviewers might actually dislike a state-machine solution as being 'non-optimal'. I guess it depends on what you're trying to optimize.
Here it is using only stdio:
#include <stdio.h>
int main(void){
char str[] = "I Like StackOverflow a lot";
int i, j = 0, lastSpace = 0;
for(i = 0;str[i]; i++){
if(!lastSpace || str[i] != ' '){
str[j] = str[i];
j++;
}
lastSpace = (str[i] == ' ');
}
str[j] = 0;
puts(str);
return 0;
}
Trimming multiple spaces also means a space should always be followed by a non space character.
int pack = 0;
char str[] = "I Like StackOverflow a lot";
for (int iter = 1; iter < strlen(str); iter++)
{
if (str[pack] == ' ' && str[iter] == ' ')
continue;
str[++pack] = str[iter];
}
str[++pack] = NULL;
int j = 0;
int k=0;
char str[] = "I Like StackOverflow a lot";
int length = strlen(str);
char str2[38];
for (int i = 0; i < length; i++)
{
if (str[i] == ' ' && str[i+1] == ' ')
continue;
str2[j] = str[i];
j++;
}
str2[j] =NULL;
cout<<str2;
void trimspaces(char * str){
int i = 0;
while(str[i]!='\0'){
if(str[i]==' '){
for(int j = i + 1; j<strlen(str);j++){
if(str[j]!=' '){
memmove(str + i + 1, str + j, strlen(str)-j+1);
break;
}
}
}
i++;
}
}
Functional variant in Haskell:
import Data.List (intercalate)
trimSpaces :: String -> String
trimSpaces = intercalate " " . words
The algorithm the next:
breaks a string up into a list of words, which were delimited by white space
concatenate the list inserting one space between each element in list
This is a very simple implementation of removing extra whitespaces.
#include <iostream>
std::string trimExtraWhiteSpaces(std::string &str);
int main(){
std::string str = " Apple is a fruit and I like it . ";
str = trimExtraWhiteSpaces(str);
std::cout<<str;
}
std::string trimExtraWhiteSpaces(std::string &str){
std::string s;
bool first = true;
bool space = false;
std::string::iterator iter;
for(iter = str.begin(); iter != str.end(); ++iter){
if(*iter == ' '){
if(first == false){
space = true;
}
}else{
if(*iter != ',' && *iter != '.'){
if(space){
s.push_back(' ');
}
}
s.push_back(*iter);
space = false;
first = false;
}
}
return s;
}
std::string tripString(std::string str) {
std::string result = "";
unsigned previous = 0;
if (str[0] != ' ')
result += str[0];
for (unsigned i = 1; i < str.length()-1; i++) {
if (str[i] == ' ' && str[previous] != ' ')
result += ' ';
else if (str[i] != ' ')
result += str[i];
previous++;
}
if (str[str.length()-1] != ' ')
result += str[str.length()-1];
return result;
}
This may be an implementation of the accepted idea.