Why Doesn't the Loop Repeat? - c

My question is this: Given a sentence, delete only the last "THE" from that sentence if it starts with the word "WE", otherwise delete all "THE"s.
my code does not delete second THE after the first THE.
What's wrong?
int main(){
char sent[100];
int i;
printf("Enter a sentence: ");
gets(sent);
if(sent[0] == 'w' && sent[1] == 'e'){
for (i=2;i<strlen(sent);i++){
if(sent[i] == 't' && sent[i+1] == 'h' && sent[i+2] == 'e' && sent[i+3] == ' '){
while( i < strlen(sent)){
sent[i] = sent[i+1];
sent[i] = sent[i+2];
sent[i] = sent[i+3];
sent[i] = sent[i+4];
i++;
}
printf("\n%s",sent);
}
}
}
else
for (i=0;i<strlen(sent);i++){
if(sent[i] == 't' && sent[i+1] == 'h' && sent[i+2] == 'e' && sent[i+3] == ' '){
while( i < strlen(sent)){
sent[i] = sent[i+1];
sent[i] = sent[i+2];
sent[i] = sent[i+3];
sent[i] = sent[i+4];
i++;
}
printf("\n%s",sent);
}
}
return 0;
}

Your problem is the inner loop to copy the remaining text:
for (i=2;i<strlen(sent);i++){
if(sent[i] == 't' && sent[i+1] == 'h' && sent[i+2] == 'e' && sent[i+3] == ' '){
while( i < strlen(sent)){
sent[i] = sent[i+1];
sent[i] = sent[i+2];
sent[i] = sent[i+3];
sent[i] = sent[i+4];
i++;
}
printf("\n%s",sent);
}
}
There are 2 main issues:
If you remove a word you must continue to search at the same location. After the loop iteration is done, the i++ is executed, advancing to the next position. Therefore you need to negate that effect by adding a i--; in case you removed an occurance of the.
Your inner loop to copy the remaining text messes up your counter for the outer loop. After you copied the text, i points to the last char terminating your loop. You must use a separate counter.
... and some more issues:
Minor issue:
You have useless assignments in the copy loop. If you assign a series of values to sent[i] only the last one will have an effect afterwards.
Minor issue:
Using strlen as loop condition causes lot of extra runtime. Only use it if necessary. For the inner loop simply check the length once and store it.
gets is evil. You should use fgets instead.
An updated version could look like this:
size_t len = strlen(sent); // Calculate limit only once
for (i=2; i < len; i++) {
if(sent[i] == 't' && sent[i+1] == 'h' && sent[i+2] == 'e' && sent[i+3] == ' ') {
size_t j = i;
while (j <= len-4) { // also copy the terminating 0 byte
sent[j] = sent[j+4];
j++;
}
printf("\n%s",sent);
len -= 4; // String was shortened
i--; // Check current position again.
}
}
Note:
You should also take into account that the last word could be the without an extra space...
You might also think about using standard C library functions like strstr...

The error that terminates your loop on the first occurrence of the word the is that the inner while loop modifies the loop variable i of the outer for loop.
for (i=0;i<strlen(sent);i++){
if(sent[i] == 't' && sent[i+1] == 'h' && sent[i+2] == 'e' && sent[i+3] == ' '){
/* after this loop i points to the end of the string */
while( i < strlen(sent)){
sent[i] = sent[i+1];
sent[i] = sent[i+2];
sent[i] = sent[i+3];
sent[i] = sent[i+4];
i++;
}
printf("\n%s",sent);
}
}
So when the while loop has moved i to the end of the string, the outer for loop will terminate as well.
This problem can be fixed by using a separate variable for the inner loop.
I also tried to fix the out-of-bounds access and moved strlen out of the loop. (still incomplete, see below)
/* the length is constant except when we remove "the " */
size_t len = strlen(sent);
for (i=0; i < len; i++){
if(sent[i] == 't' && sent[i+1] == 'h' && sent[i+2] == 'e' && sent[i+3] == ' '){
/* use separate variable for inner loop */
int j = i;
/* limit len-3 tp prevent sent[j+4] from reading past the NUL ('\0') character */
while( j < len-3){
/* useless assignments to the same array element removed */
sent[j] = sent[j+4];
j++;
}
len -= 4; /* we removed 4 characters */
printf("\n%s",sent);
}
}
This code has still the problem that it doesn't remove "the the " because the for loop won't check the same position again after removing "the ".
One option would be to decrement i after removing "the ". If the string starts with "the the " this would temporarily set i to -1. It will be incremented again by the for statement.
I prefer to increment i only if "the " was not found.
That's why I move the i++ from the for statement into the loop body.
/* the length is constant except when we remove "the " */
int len = strlen(sent);
for (i=0; i < len; /* no i++ here */){
if(sent[i] == 't' && sent[i+1] == 'h' && sent[i+2] == 'e' && sent[i+3] == ' '){
/* use separate variable for inner loop */
int j = i;
/* limit len-3 tp prevent sent[j+4] from reading past the NUL ('\0') character */
while( j < len-3){
/* useless assignments to the same array element removed */
sent[j] = sent[j+4];
j++;
}
len -= 4; /* we removed 4 characters */
printf("\n%s",sent);
} else {
/* advance to the next character only if we didn't just remove "the " */
i++;
}
}
The while loop can also be written as a for loop.
/* the length is constant except when we remove "the " */
int len = strlen(sent);
for (i=0; i < len; /* no i++ here */){
if(sent[i] == 't' && sent[i+1] == 'h' && sent[i+2] == 'e' && sent[i+3] == ' '){
/* use separate variable for inner loop */
/* limit len-3 tp prevent sent[j+4] from reading past the NUL ('\0') character */
for(int j = i; j < len-3; j++){
/* useless assignments to the same array element removed */
sent[j] = sent[j+4];
}
len -= 4; /* we removed 4 characters */
printf("\n%s",sent);
} else {
/* advance to the next character only if we didn't just remove "the " */
i++;
}
}
Note: You can write the loops in various different ways, so there are other valid solutions, as you can see from the comments.

Related

What's wrong with this string checking program?

I want to make this program only English in the string, but when the end of the string ends in English, another strange word is added. Why is that?
int main(void)
{
char line[100];
char line2[100];
printf("Please enter a string: ");
gets_s(line, sizeof(line));
int k = 0;
for(int i = 0; line[i] != '\0'; ++i) {
while(line[i] >= 'A'&& line[i] <= 'z') {
line2 [k++] = line[i++];
}
}
line2[k] = '\0';
printf("string: %s\n", line2);
return 0;
}
for(int i = 0; line[i] != '\0'; ++i) {
while(line[i] >= 'A'&& line[i] <= 'z') {
line2 [k++] = line[i++];
}
}
replacing the for loop with a while loop...
int i = 0;
while (line[i] != '\0') {
while (line[i] >= 'A' && line[i] <= 'z') {
line2 [k++] = line[i++];
}
i++;
}
So, here you can see if the inner while goes to the '\0' i gets incremented past the terminating zero byte.
The basic problem is that you put responsibility for incrementing i in two different places. As a result, it can get incremented more than you want -- for example, so as to skip over the string terminator.
It appears to me that you have caused this problem for yourself by making the code more complicated than it needs to be. Why use a two-loop nest to iterate over a single string? In fairness, there are indeed potential reasons to do that, but none of them appear to be in evidence here. I suggest changing the inner while loop to an if statement, and not incrementing i within:
for (int i = 0; line[i] != '\0'; ++i) {
if (line[i] >= 'A' && line[i] <= 'z') {
line2[k++] = line[i];
}
}
Note how there is only one place where i is incremented in that variation.
Note also that there may be characters that satisfy c >= 'A' && c <= 'z' but are not letters. I say "may" because C is not specific to a particular character set or encoding, but in practice, it is virtually certain that there are non-letter characters in that range on your implementation. Perhaps isalpha((unsigned char) line[i]) would be a better fit, though it is not, by itself, without character set related issues.

What does str[i - 1] == ' ' mean?

I've been reviewing a program that capitalises the first letter of every word in a string. For example, "every single day" becomes "Every Single Day".
I don't understand the part str[i - 1] == ' '. What does that do?
#include <stdio.h>
char *ft_strcapitalize(char *str)
{
int i;
i = 0;
while (str[i] != '\0')
{
if ((i == 0 || str[i - 1] == ' ') &&
(str[i] <= 'z' && str[i] >= 'a'))
{
str[i] -= 32;
}
else if (!(i == 0 || str[i - 1] == ' ') &&
(str[i] >= 'A' && str[i] <= 'Z'))
{
str[i] += 32;
}
i++;
}
return (str);
}
int main(void)
{
char str[] = "asdf qWeRtY ZXCV 100TIS";
printf("\n%s", ft_strcapitalize(str));
return (0);
}
i is the index in the string of the current character you are thinking about capitalising (remembering it starts at 0).
i-1 is the index in the string of the previous character to the one you are considering.
str[i-1] is the character in the position previous to the one you are considering.
== ' ' is comparing that character to a space character.
So str[i-1] == ' ' means "Is the character to the left of this one a space?"
It is checking for spaces, or more exactly, the line
if ((i == 0 || str[i - 1] == ' ')
Checks if we are looking at the string beginning or its previous line was a space, that is, to check if a new word was encountered.
In the string "every single day", i = 0 at the bold position, and in the next case,
"every single day", i = 6 and str[i-1] is ' ' marking a new word was encountered
"What does str[i - 1] == ' ' mean?"
' ' is a character constant for the white space character (ASCII value 32).
str is a pointer to char in the caller. (Practically thinking, it should point to an array of char with a string inside of it, not just a single char).
i is a counter.
Note that the C syntax allows that you can use array notation for pointers. Thus, str[1] is equal to *(str + 1).
The [i - 1] in str[i - 1] means that you access the element before the element str[i] is pointing to.
The element str[i - 1] is pointing to, is compared to the white space character (If the element str[i - 1] is pointing to actually contains white space).
The condition evaluates to true if this is the case, else the condition is false.
Side Notes:
Note that str[i - 1] can be dangerous when i == 0. Then you would try to access memory beyond the bounds of the pointed array. But in your case, this is secure since str[i - 1] == ' ' is only evaluated, if i == 0 is not true, thanks to the logical OR ||.
if ((i == 0 || str[i - 1] == ' ')
So this case is considered in your code.
str[i] -= 32; is equivalent to str[i] -= 'a' - 'A';. The latter form can improve readability as the capitalizing nature is brought to focus.
Here you are comparing str[i-1] with character space, Whose ASCII code is 32.
e.g.
if(str[i-1] == ' ')
{
printf("Hello, I'm space.\n");
}
else
{
printf("You got here, into the false block.\n");
}
Execute this snippet and if the comparison yields the value 1 it's true, false otherwise. Put str[] = "Ryan Mney"; and then compare you'll understand, what is happening?
The C-language provides a number of useful character macros that can be used to both make code more portable, and more readable. Although the sample code you are reviewing does not use these macros, please consider using these macros to make your code more portable, more robust, and easier for others to read.
Please use the islower/isupper/isalpha and tolower/toupper macros; these ctype macros make C-language string processing easier to read.
islower(ch) - check whether ch is lower case
isupper(ch) - check whether ch is upper case
isalpha(ch) - check whether ch is alphabetic (lower or upper case)
tolower(ch) - convert ch to lower case (if it is alphabetic)
toupper(ch) - convert ch to upper case (if it is alphabetic)
Yes, they are macros -
What is the macro definition of isupper in C?
The C-language provides the 'for' control statement which provides a nice way to express string processing. Simple indexed loops are often written using 'for' rather than 'while'.
#include <ctype.h>
char*
ft_strcapitalize(char *str)
{
for( int i=0; (str[i] != '\0'); i++ )
{
if ((i == 0 || isspace(str[i - 1])) && islower(str[i]) )
{
str[i] = toupper(str[i]);
}
else if (!(i == 0 || str[i - 1] == ' ') && isupper(str[i]) )
{
str[i] = tolower(str[i]);
}
}
return (str);
}
A slight refactoring makes the code a bit more readable,
char*
ft_strcapitalize(char *str)
{
for( int i=0; (str[i] != '\0'); i++ )
{
if( (i == 0 || isspace(str[i - 1])) )
{
if( islower(str[i]) ) str[i] = toupper(str[i]);
}
else if( !(i == 0 || isspace(str[i - 1]) )
{
if( isupper(str[i]) ) str[i] = tolower(str[i]);
}
}
return(str);
}
Alternately, use isalpha(ch),
char*
ft_strcapitalize(char *str)
{
for( int i=0; (str[i] != '\0'); i++ )
{
if( (i == 0 || isspace(str[i - 1])) )
{
if( isalpha(str[i]) ) str[i] = toupper(str[i]);
}
else if( !(i == 0 || isspace(str[i - 1]) )
{
if( isalpha(str[i]) ) str[i] = tolower(str[i]);
}
}
return(str);
}
Simplify the conditional expression even further, by performing the special case (first character of string) first.
char*
ft_strcapitalize(char *str)
{
if( islower(str[0]) ) str[0] = toupper(str[0]);
for( int i=1; (str[i] != '\0'); i++ )
{
if( isspace(str[i - 1]) )
{
if( islower(str[i]) ) str[i] = toupper(str[i]);
}
else if( !isspace(str[i - 1]) )
{
if( isupper(str[i]) ) str[i] = tolower(str[i]);
}
}
return(str);
}
Again, the alternate isalpha(ch) version,
char*
ft_strcapitalize(char *str)
{
if( isalpha(str[0]) ) str[0] = toupper(str[0]);
for( int i=1; (str[i] != '\0'); i++ )
{
if( isspace(str[i - 1]) )
{
if( isalpha(str[i]) ) str[i] = toupper(str[i]);
}
else if( !isspace(str[i - 1]) )
{
if( isalpha(str[i]) ) str[i] = tolower(str[i]);
}
}
return(str);
}
Even more idiomatic, just use a 'state' flag that indicates whether we should fold to upper or lower case.
char*
ft_strcapitalize(char *str)
{
int first=1;
for( char* p=str; *p; p++ ) {
if( isspace(*p) ) {
first = 1;
}
else if( !isspace(*p) ) {
if( first ) {
if( isalpha(str[i]) ) str[i] = toupper(str[i]);
first = 0;
}
else {
if( isalpha(str[i]) ) str[i] = tolower(str[i]);
}
}
}
return(str);
}
And your main test driver,
int main(void)
{
char str[] = "asdf qWeRtY ZXCV 100TIS";
printf("\n%s", ft_strcapitalize(str));
return (0);
}
' ' is a character constant representing the value of the space character in the execution set. Using ' ' instead of 32 increases both readability and portability to systems where space might not have the same value as in the ASCII character set. (i == 0 || str[i - 1] == ' ') is true if i is the offset of the beginning of a word in a space separated list of words.
It is important to try and make the as simple and readable as possible. Using magic constants like 32 is not recommended when a more expressive alternative is easy and cheap. For example you convert lowercase characters to uppercase with str[i] -= 32: this magic value 32 (again!) happens to be the offset between the lowercase and the uppercase characters. It would be more readable to write:
str[i] -= 'a' - 'A';
Similarly, you wrote the range tests for lower case and upper case in the opposite order: this is error prone and surprising for the reader.
You are also repeating the test for the start of word: testing for lower case only at the start of word and testing for upper case otherwise makes the code simpler.
Finally, using a for loop is more concise and less error prone than the while loop in your function, but I known that the local coding conventions at your school disallow for loops (!).
Here is a modified version:
#include <stdio.h>
char *ft_strcapitalize(char *str) {
size_t i;
i = 0;
while (str[i] != '\0') {
if (i == 0 || str[i - 1] == ' ') {
if (str[i] >= 'a' && str[i] <= 'z') {
str[i] -= 'a' - 'A';
}
} else {
if (str[i] >= 'A' && str[i] <= 'Z') {
str[i] += 'a' - 'A';
}
}
i++;
}
return str;
}
int main(void) {
char str[] = "asdf qWeRtY ZXCV 100TIS";
printf("\n%s", ft_strcapitalize(str));
return 0;
}
Note that the above code still assumes that the letters form two contiguous blocks in the same order from a to z. This assumption holds for the ASCII character set, which is almost universal today, but only partially so for the EBCDIC set still in use in some mainframe systems, where there is a constant offset between cases but the letters from a to z do not form a contiguous block.
A more generic approach would use functions and macros from <ctype.h> to test for whitespace (space and other whitespace characters), character case and to convert case:
#include <ctype.h>
char *ft_strcapitalize(char *str) {
for (size_t i = 0; str[i] != '\0'; i++) {
if (i == 0 || isspace((unsigned char)str[i - 1]))
str[i] = toupper((unsigned char)str[i]);
else
str[i] = tolower((unsigned char)str[i]);
}
return str;
}

Program to find vowels, If statements not assigning correct values [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 3 years ago.
Improve this question
I have a simple program to find the vowels in a string. The for loop is supposed to iterate through the string and see if the char matches any of the vowels using and if else block but the output is just 100 As.
I tried making them all just ifs but that gave all Us.
#include <stdio.h>
int main()
{
const int SIZE = 100;
char str[SIZE] = {"the brown fox\0"};
char vowels[SIZE];
for (int i = 0; i <= SIZE; i++) {
if (str[i] == '97' || '65') {
vowels[i] = 'a';
}
else if (str[i] == '101' || '69' ) {
vowels[i] = 'e';
}
else if (str[i] == '105' || '73') {
vowels[i] = 'i';
}
else if (str[i] == '111' || '81') {
vowels[i] = 'o';
}
else if (str[i] == '117' || '85') {
vowels[i] = 'u';
}
printf("%c", vowels[i]);
}
return 0;
}
EDIT: Fixed the assignment if e.g. (str[i] == '97' || str[i] == '65') now it's printing strange symbols
EDIT 2: New code
#include <stdio.h>
int main()
{
const int SIZE = 100;
char str[SIZE] = {"the brown fox\0"};
char vowels[SIZE];
for (int i = 0; i <= SIZE; i++) {
if (str[i] == 'a' || str[i] == 'A') {
vowels[i] = 'a';
}
else if (str[i] == 'e' || str[i] =='E' ) {
vowels[i] = 'e';
}
else if (str[i] == 'i' || str[i] == 'I') {
vowels[i] = 'i';
}
else if (str[i] == 'O' || str[i] == 'o') {
vowels[i] = 'o';
}
else if (str[i] == 'u' || str[i] == 'U') {
vowels[i] = 'u';
}
printf("%c", vowels[i]);
}
return 0;
}
EDIT 3: Even after initialing vowels to '' at the start of the loop as suggested the strange symbols are gone but it's still not functioning properly.
You are comparing your char str[i] with '97'
6.4.4.4
An integer character constant has type int. The value of an integer character constant containing a single character that maps to a single-byte execution character is the numerical value of the representation of the mapped character interpreted as an integer. The value of an integer character constant containing more than one character (e.g., 'ab'), or containing a character or escape sequence that does not map to a single-byte execution character, is implementation-deļ¬ned.
If you want to compare a char you can use the ascii value for example 97 or directly the char with 'c'.
For more maintenability and readability I prefer using the char directly.
There is other problems in your code:
First, in your for loop: for (int i = 0; i <= SIZE; i++) {
You are going too far in your array because of your <= as arrays id starts with 0, if you type str[100], in reality you are using the 101st char.
Another problem is your if statements: if (str[i] == '97' || '65') {
Here your if statement is equivalent to if (str[i] == '97' || '65' != 0) {
Consider retyping str[i] == : if (str[i] == '97' || str[i] == '65') {
Plus don't forget the first problem I mentionned about your '97'
You have a very large number of small problems summarized below:
#define SIZE 100 /* if you need a constant, #define one (or more) */
...
char vowels[SIZE] = ""; /* initialize all zero, {0) is valid also */
An integer constant is created by #define or by use of an enum. A const qualified int is not a constant integer. (that said VLAs are legal in C99, but optional in C11)
int idx = 0; /* separate index for filling vowels array */
Keep a separate index for filling the vowels array.
/* don't use magic-numbers in your code */
if (str[i] == 'a' || str[i] == 'A') {
Don't use magic-numbers, instead, use literal character constants were needed in your code to produce much more readable code.
Your program takes arguments, use them to pass the string to parse (or read from stdin), e.g.
int main (int argc, char **argv) {
const char *str = (argc > 1) ? argv[1] : "the brown fox";
...
The test ? if_true : if_false operator is called the ternary operator. It allows a simple in-line conditional to select one of two values based on the test condition (e.g. (argc > 1))
If you plan on using vowels as a string, don't forget to nul-terminate vowels after the loop, e.g.
vowels[idx] = 0; /* nul-terminate vowels */
Correcting all the errors and adding the arguments to main() you could do something similar to:
#include <stdio.h>
#define SIZE 100 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
const char *str = (argc > 1) ? argv[1] : "the brown fox";
char vowels[SIZE] = ""; /* initialize all zero, {0) is valid also */
size_t idx = 0; /* separate index for filling vowels array */
for (int i = 0; idx < SIZE - 1 && str[i]; i++) {
/* don't use magic-numbers in your code */
if (str[i] == 'a' || str[i] == 'A') {
vowels[idx++] = 'a'; /* assign 'a', increment index */
}
else if (str[i] == 'e' || str[i] == 'E' ) {
vowels[idx++] = 'e';
}
else if (str[i] == 'i' || str[i] == 'I') {
vowels[idx++] = 'i';
}
else if (str[i] == 'o' || str[i] == 'O') {
vowels[idx++] = 'o';
}
else if (str[i] == 'u' || str[i] == 'U') {
vowels[idx++] = 'u';
}
}
vowels[idx] = 0; /* nul-terminate vowels */
printf (" %zu vowels: ", idx); /* print number of vowels */
for (int i = 0; vowels[i]; i++) /* output each vowel, comma-separated */
printf (i > 0 ? ", %c" : "%c", vowels[i]);
putchar ('\n'); /* tidy up with newline */
return 0;
}
Example Use/Output
bin\vowels.exe "a quick brown fox jumps over the lazy dog"
11 vowels: a, u, i, o, o, u, o, e, e, a, o
Depending on your compiler str[i] == '117' (and the rest) may give you an error as signle quotes are only to be used when you want to implement the ascii equivalent of a single character like 'a' or so. Therefore str[i] == '117' is checking if str[i] is equal to the ascii equivalent of "117".
Other than that " || " is a logical "or" operator. When you write down str[i] == '111' || '81' you simply mean "find ascii codes of 111 and 81(which dont exist) , use them in "or" operation, check if the result equals str[i]".
last but not least i found a nice function online which might help making your code more compact
int isvowel(int ch)
{
int c = toupper(ch);
return (c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U');
}
to explain it shortly, if the char equivalent of given int ch is lowercase, the function changes it to uppercase and checks if it is an uppercase vowel, if the given integer already equals an uppercase vowel int c = toupper(ch); doesnt change anything.
Implementation can be done as:
for(int i=0; i<SIZE; i++) //scan all emelents of str[SIZE]
{
if(isvowel(str[i])) //print if vowel
printf ("%c", str[i]);
}

Two spaces between sentences

I have a code which finds if there is more than one space between the words, in that case change them to one.
And I need to add some additional function which should make two spaces between sentences.
(A sentence's last symbol is . )
For example.
if i have file with text:
This is my first program. Hello world
program should print me:
This is my first program. Hello world
Code:
# include <stdio.h>
# include <stdlib.h>
int main()
{
FILE *in;
char myStr[100],newStr[100];
int ch;
int j,i,k,z=0;
in=fopen("duom.txt","r");
if(in){
while(EOF != ch){
ch=fgetc(in);
myStr[z] = ch;
z++;
k=0;
for(i=0; myStr[i] != '\0'; i++) {
if(myStr[i-1] != '.' && myStr[i] == ' ' && myStr[i+1] == ' ' )
continue;
newStr[k]= myStr[i];
k++;
}
}
}
for(j=0;j<k;j++){
printf("%c",newStr[j]);
}
printf("\n");
fclose(in);
system("pause");
return 0;
}
I don't ask you to write my whole code, just give me some ideas.
Sorry for my bad english :/
This loop follows your general approach of processing the file in blocks:
Your Approach Revised:
# include <stdio.h>
# include <stdlib.h>
int main() {
FILE *in;
char myStr[100],newStr[100];
int ch;
int j,i,k,z=0;
in=fopen("duom.txt","r");
if(!(in)) { fprintf(stderr,"Error opening file!\n"); }
else { //the file was opened
int go = 1; //master loop control
while(go) { //master loop
z = 0; //set sub loop
ch = '\0';//control variables
while(z < 100 && EOF != ch){ //process file in 99 character blocks
ch=fgetc(in); //getting one character at a time
if(EOF == ch) { go = 0; } //break master loop
else { myStr[z++] = ch; } //or process char
}
myStr[z] = '\0'; //null terminate the string
for(i=0; myStr[i] != '\0'; i++) {
//i=99='\0' <-- assumed is highest string size
//if i=0; Do you really want that leading space?
if(i== 0 && myStr[i] == ' ' ) { continue; }
//if i=98 it is the last char in the string i=99 should be '\0'
//So do you really want that trailing space?
if(i==98 && myStr[i] == ' ' ) { continue; }
//Same rational as above.
//So do you really want those trailing 2 spaces?
if(i==97 && myStr[i] == ' ' && myStr[i+1] == ' ') { continue; }
//if i=0; myStr[i-1] will likely cause a segmentation fault
if(i > 0 && myStr[i] == ' ' && myStr[i+1] == ' ' && myStr[i-1] != '.') { continue; }
newStr[k] = myStr[i];
k++;
}
for(j=0;j<k;j++){ printf("%c",newStr[j]); } //print the 99 char block
}
printf("\n"); //print a newline for good measure
fclose(in); //close file
}
return 0;
}
Note that the code will misbehave for files with size greater then 99 chars because spacing format comparisons are not be made from the end of one 99 char block to the beginning of another. You could implement this by not deleting the leading/trailing spaces comparing the values at i=1 & i=2 with the last two chars at i=97 & i=98 in the previous block.
This is a different, better loop. It solves the block barrier issues of the other approach and uses much less memory
Better approach:
# include <stdio.h>
# include <stdlib.h>
int main() {
FILE *in;
in=fopen("duom.txt","r");
if(!(in)) { fprintf(stderr,"Error opening file!\n"); return -1; }
//the file was opened
int x; //stores current char
int y; //stores previous char
for(y='\0'; (x=fgetc(in)) != EOF; y=x) { //read in 'x' until end of file
// The following conditions cover all cases:
// is 'x' not a space? Then print 'x'
// is 'x' a space but 'y' a period? Then print two spaces
// is 'x' a space and 'y' not a period but also not a space? Then print a space
// Otherwise 'x' is part of extra spacing, do nothing
if(x != ' ') { printf("%c",x); }
else if(x == ' ' && y == '.') { printf(" "); }
else if(x == ' ' && y != '.' && y != ' ') { printf(" "); }
else { ; } //do nothing
}
printf("\n"); //print a newline for good measure
fclose(in); //close file
return 0;
}
I suggest using strtok() and concatenate the tokens together separated by the correct number of spaces. If a token ends with a period, use two spaces. Otherwise, only use one. This way you don't even need to check how many spaces are in between words.

How to replace specific characters in a string with other characters

I am trying to make a program that will take as input a string and replace all the vowels with the * symbol. So, for "hello world", star_vowels should return "h*ll* w*rld".
What I have for code so far is:
int star_vowels(char s[]){
int j;
j = 0;
while (s[j] != '0'){
j++;
if (s[j] = 'a' || s[j] == 'e' || s[j] == 'i' || s[j] == 'o' || s[j] == 'u'){
putchar('*');
} else {
putchar(j);
}
return 0;
}
}
This code has a number of things wrong with it.
1) while (s[j] != '0')
I'm fairly sure you want to be checking for the NUL character, not the character constant zero. Change '0' to '\0'
2) j++
you are incrementing j before you even look at the 0th index of your array. If you had a vowel at s[0], this would be missed. Move j++ to the very bottom of the while loop, just before the ending brace.
3) s[j] = 'a'
You are using the assignment operator = here when you should be using the equality operator == instead. Using the assignment operator is legal C code and thus will compile. Unfortunately it will return true and you'll end up replacing all of your characters with asterisks
4) putchar(j);
You are attempting to output 'j' (your iterator) when you really want to be outputting s[j] (your character).
5) return 0
just like in #2, your return statement is in the wrong place. You have it within the while loop when it should be outside of it. The way you have it written, the while loop will only execute the first iteration before your function goes out of scope.
int star_vowels(char s[]) {
int j = 0;
while (s[j] != '\0'){
if (s[j] == 'a' || s[j] == 'e' || s[j] == 'i' || s[j] == 'o' || s[j] == 'u') {
putchar('*');
} else {
putchar(s[j]);
}
j++;
}
return 0;
}
I think your question is going to be 'everything is *' and that's because of this part of your giant if:
if (s[j] = 'a'
That will always be true. You need the ==
Also you put j++ too early - you'd skip characters because you immediately increment upon entering the loop.
By incrementing the j at the beginning you will lose the 0 index (the first character).
Since you want to return new data to the outside world (outside the function scope), you either allocate memory for the new data outside the function, and pass in a pointer of that data to this function, or you just allocate dynamic memory inside this function - remember to delete it.
One implementation would be:
char *star_vowels(char s[]){
// let's allocate memory for the new string.
// its size should be strlen(s) + 1 (the ending char).
char *final = malloc(strlen(s) + 1);
int j = 0;
while (s[j] != 0){
if (s[j] == 'a' || s[j] == 'e' || s[j] == 'i' || s[j] == 'o' || s[j] == 'u'){
final[j] = '*';
} else {
final[j] = s[j];
}
j++;
}
final[j] = 0; // end the string
return final;
}
Working example: http://codepad.org/dd2w5cuy

Resources