I am trying to find out how many characters are in the given array except blanks
but it's not working, k supposed to count blanks and substract them from i[characters + blanks] but it doesn't.
int i= 0;
int n= 0;
int k= 0;
char c[256] = {};
fgets(c ,256, stdin);
while(c[i] != '\0' ){
if(c[i] == ' '){
i++;
k++;
continue;}
i++;}
printf("%d",i-k);
Few observation, here
fgets(c ,256, stdin);
fgets() stores \n at the end of buffer if read. From the manual page of fgets()
If a newline is read, it is stored into the buffer.
A terminating null byte ('\0') is stored after the last character in
the buffer
Remove the trailing \n first and then iterate over it. For e.g
fgets(c, sizeof(c), stdin);
c[strcspn(c, "\n")] = 0; /* remove the trailing \n */
Also use of continue is not required here i.e you can achieve the task without using it. For e.g
int main(void) {
int i= 0;
int k= 0;
char c[256] = ""; /* fill whole array with 0 */
fgets(c, sizeof(c), stdin);
c[strcspn(c, "\n")] = 0; /* remove the trailing \n */
while(c[i] != '\0' ){ /* or just c[i] */
if(c[i] == ' ') {
k++; /* when cond is true, increment cout */
}
i++; /* keep it outside i.e spaces or not spaces
this should increment */
}
printf("spaces [%d] without spaces [%d]\n",k,i-k);
return 0;
}
Related
I'm learning string in c, and i'm working on my homework which ask me to write a program to replace part of string under certain circumstances. Here is my source code(undone):
#include <stdio.h>
#include <string.h>
int main()
{
char str1[128], str2[128], str3[128];
for (int i = 0; i < 128; i++) //initialize str
{
str1[i] = 0;
str2[i] = 0;
str3[i] = 0;
}
printf("Input the first string:"); //inputs
fgets(str1, 128, stdin);
printf("Input the second string:");
fgets(str2, 128, stdin);
printf("Input the third string:");
fgets(str3, 128, stdin);
if (strncmp(str1, str2, strlen(str2) - 1) == 0) //if the first n charters match (n=length of str2)
{
printf("%s", str3); //print str3
int RemainingChar = 0;
RemainingChar = strlen(str1) - strlen(str2);
for (int i = 0; i < RemainingChar; i++)
{
printf("%c", str1[i + strlen(str2) - 1]); //print the remaining part
}
}
return 0;
}
Here is how it run:
Input the first string:asdfghjkl
Input the second string:asd
Input the third string:qwe
qwe
fghjkl
There is an unexpected line break. what should I do to make it output like this:qwefghjkl?
The function fgets will also store the newline character '\n' at the end of the line into the string. If you don't want this newline character to be printed, then you must remove it before printing the string.
See the following question for several ways to remove the newline character:
Removing trailing newline character from fgets() input
The output of my code is incorrect. For example, if I input "joy is joyful", then want to remove the word "joy", the output should be " is joyful", but instead the output is the same as the input.
Here is the full code:
#include<stdio.h>
#include<conio.h>
#include<string.h>
void print(char string[100]);
main()
{
char string[100], remove[100];
int stringLen, removeLen, i, j, k, l, count, location, sLen, ij, counter = 0;
printf("Enter any string: ");
gets(string);
printf("Enter word to remove: ");
gets(remove);
printf("\nString before removing '%s': ", remove);
print(string);
stringLen = strlen(string);
sLen = stringLen;
removeLen = strlen(remove);
for(i=0; i<stringLen; i++)
{
count = 0;
for(j=0; j<removeLen; j++)
{
if(string[i+j] == remove[j])
{
count++;
location = i;
ij = i+j;
}
}
if(count == removeLen)
{
if(string[ij+1] == '\0' && string[ij+1] == ' ' && string[ij+1] == '\n')
{
counter = count;
}
else
{
counter = count - 1;
}
}
if(counter == removeLen)
{
for(l=0; l<count; l++)
{
for(k=location; k<sLen; k++)
{
string[k] = string[k+1];
}
sLen--;
}
}
}
printf("\n\nString after removing '%s':", remove);
print(string);
getch();
return 0;
}
void print(char string[100])
{
printf("\n%s", string);
}
I tried making this part:
if(count == removeLen)
{
if(string[ij+1] == '\0' && string[ij+1] == ' ' && string[ij+1] == '\n')
{
counter = count;
}
else
{
counter = count - 1;
}
}
To this and it worked:
if(count == removeLen)
{
if(string[ij+1] != '\0' && string[ij+1] != ' ' && string[ij+1] != '\n')
{
counter = count - 1;
}
else
{
counter = count;
}
}
What seems to be the problem with the original one?
For pf all, never, ever, EVER, use gets(). It is so prone to exploit by buffer overrun that is have been removed from the C-library beginning with C11. For more discussion see: Why gets() is so dangerous it should never be used!
In your word replacement, you are not worrying about removing leading or trailing whitespace before or after the word you remove and you only remove that word if it is not a substring in a larger word or a word followed by punctuation. (this is fine -- but in isolating an removing words you will generally want to takes what is left into consideration)
You can simplify what you are attempting to do and reduce the complete algorithm to a single loop over the character in the string. You simply keep three indexes (or counters if you want to think of it that way). You need a read-index, the next character to be read. You need a write-index, the next location in the string to be written. And finally you need a remove-index to the characters in the substring to be removed.
Here you simply loop over the characters in the string with your read-index. Your read and write indexes begin the same. If a letter matches the first letter in your remove substring, you increment your remove-index and loop again. If a sequence of characters match all characters in your remove substring, on the next iteration your substring index will be at its nul-terminating character.
Now you can test if the next character under the read-index in your string is a space (using the isspace() macro) or testing if you are at the end of your original string. If either case is true, you simply subtract the substring length from your write-index and continue on -- effectively removing the substring from your original string. There are no multiple-loops needed, you are essentially working through each character of the original keep track of where you are (the state) with the substring index.
A short example approaching it this way could be something like the following. The function remove_substr(), reads the characters in str and removes each isolated occurrence of substr within it updating the original str in-place:
int remove_substr (char *str, const char *substr)
{
if (!strstr (str, substr)) /* if substr not found in str */
return 0; /* return 0 - nothing replaced */
size_t sslen = strlen (substr), /* length of substr */
i = 0, j = 0, n = 0; /* read, write, substr indexes */
do { /* loop over str (including '\0') */
if (!substr[n]) { /* substr found (at substr '\0') */
/* if at end of str or whitespace */
if (!str[i] || isspace((unsigned char)str[i]))
j -= sslen; /* subtract sslen from write index */
n = 0; /* reset substr index */
}
str[j++] = str[i]; /* copy from read to write index */
if (str[i] == substr[n]) /* if char matches substr */
n++; /* increment substr counter */
} while (str[i++]); /* exit after '\0' processed */
return 1; /* return replacements made */
}
A simple type int was chosen for the return type to indicate 0 no removals took place, or 1 indicating that occurrences of substr were removed from str.
A short example calling the function could be:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXC 1024
/* insert function here */
int main (void) {
char str[MAXC] = "", /* storage for string */
substr[MAXC] = ""; /* storage for substring */
fputs ("enter string: ", stdout); /* prompt for string */
if (!fgets (str, sizeof str, stdin)) /* read/validate input */
return 1;
str[strcspn(str, "\n")] = 0; /* overwrite '\n' with '\0' */
fputs ("enter substr: ", stdout); /* ditto for substr */
if (!fgets (substr, sizeof substr, stdin))
return 1;
substr[strcspn(substr, "\n")] = 0;
if (remove_substr (str, substr)) /* remove all substr in str */
printf ("\nresult: '%s'\n", str); /* output updated str if removals */
else
puts ("\nno replacements made"); /* otherwise output no replacements */
}
Simply run the program and you will be prompted to input the string and the substring to remove. Currently each of the strings used are limited to MAXC (1024 characters), adjust to meet your needs -- but don't skimp on buffer size.
Example Use/Output
$ ./bin/str_rm_substr
enter string: joy is joyful
enter substr: joy
result: ' is joyful'
A more complicated example:
$ ./bin/str_rm_substr
enter string: joy is joyful, joy is full of joy
enter substr: joy
result: ' is joyful, is full of '
There are many ways to write a function like this. You can use combinations of strtok() to tokenize a copy of your original string checking whether each token matches your substr to remove. You can inch-worm down your string using multiple loops to scan forward to find the first letter in your substr and then loop to see if it matches. You can also use combinations of strspn() and strcspn() to do the same inch-worm technique, letting those function handle looping for you. There are probably a 1/2-dozen or so valid approaches.
Look things over and let me know if you have questions.
Here
if(string[ij+1] == '\0' && string[ij+1] == ' '
you test if a character is both a nul and a space.
That will never be true. In order words, the whole if-statement is useless as it always takes the false path.
The problem is in this case if(string[ij+1] == '\0' && string[ij+1] == ' ' && string[ij+1] == '\n') and the counter decreasing. So after decreasing you never will get into this code:
if(counter == removeLen)
{
for(l=0; l<count; l++)
{
for(k=location; k<sLen; k++)
{
string[k] = string[k+1];
}
sLen--;
}
}
So remove this code:
if(count == removeLen)
{
if(string[ij+1] == '\0' && string[ij+1] == ' ' && string[ij+1] == '\n')
{
counter = count;
}
else
{
counter = count - 1;
}
}
And it will be work.
So I am trying to write a program that takes a sentence and prints it out from the third word. Ex one two three four should print out three four.
Now this code works but I have no idea why as the logic under the else statement make it seem like it should not.
Would be thankful if someone could explain why it works like this.
Here is the code:
#include <stdio.h>
#include <string.h>
#define SIZE 100
int main(void) {
char arr[SIZE];
char *point;
char again = 'n';
do {
int count = 0;
for (int i = 0; i < SIZE; i++) {
arr[i] = '\0';
}
printf("Enter a sentence:");
gets(arr);
for (int i = 0; i < SIZE; i++) {
if (arr[i] == ' ') {
count++;
}
}
if (count < 2) {
printf("The sentence is to short!\n");
} else {
count = 1; //shouldn't this be count = 0?
for (int i = 0; i < SIZE; i++) {
if (arr[i] == ' ') {
count++;
}
if (count == 2) {
point = &arr[i + 2]; //shouldn't this be [i+1]?
}
}
printf("%s\n", point);
}
printf("Do you want to try again? (y/n)");
scanf("%c", &again);
while (getchar() != '\n');
} while (again == 'y' || again == 'Y');
return 0;
}
Your code has multiple problems:
You should never use gets(). This function has been removed from the C Standard because it cannot be given the maximum number of characters to write to the destination buffer, so any sufficiently long line from the input stream will cause undefined behavior. This is a classic security flaw. Use fgets() instead.
The loop while (getchar() != '\n'); will cause an infinite loop if there is no newline before the end of file, which will happen if you redirect input an empty file. You should also check for EOF:
while ((c = getchar()) != EOF && c != '\n')
continue;
There is no need to initialize the destination array, but you should check if the input operation succeeded, by comparing the return value of fgets() to NULL.
When iterating through the array to count spaces, you should stop at the null terminator. The contents of the array beyond the null terminator is indeterminate after the input operation, even if you initialized it prior to the call.
The code to skip the words is cumbersome and not easy to validate. Indeed point = &arr[i+2]; should be point = &arr[i+1].
words might be separated by more than one space, and initial spaces should be ignored.
Here is a corrected version using string functions strspn and strcspn to skip blanks and non-blanks:
#include <stdio.h>
#include <string.h>
#define SIZE 100
#define WS " \t\n\r\v\f" /* white space characters */
int main(void) {
char arr[SIZE];
char *p;
for (;;) {
printf("Enter a sentence:");
if (fgets(arr, sizeof arr, stdin) == NULL)
break;
p = arr;
p += strspn(p, WS); /* skip initial spaces */
p += strcspn(p, WS); /* skip first word */
p += strspn(p, WS); /* skip spaces */
p += strcspn(p, WS); /* skip second word */
p += strspn(p, WS); /* skip spaces */
if (*p == '\0') {
printf("The sentence is too short!\n");
} else {
printf("%s", p);
}
printf("Do you want to try again? (y/n)");
if (fgets(arr, sizeof arr, stdin) == NULL)
break;
if (*arr != 'y' && *arr != 'Y')
break;
}
return 0;
}
Another simple way to handle the word count is to walk-a-pointer down your string in a state loop keeping track of whether you are in a word (if so increase word count), otherwise you are not in a word and just keep walking down the buffer (i.e. iterating over each char) until you find the next word (or end of string).
The logic is simple, after filling your buffer, and setting a pointer to it, e.g.
#define MAXC 1024 /* buffer size (don't skimp) */
#define NWORD 3 /* output beginning with NWORD word */
...
char buf[MAXC] = "", /* buffer to hold line */
*p = buf; /* pointer to walk down buffer */
int n = 0, /* word counter */
in = 0; /* flag - in a word */
Just loop checking each character with isspace() and handle setting your in flag to either 1 (in word) or 0 (in space before or between words) incrementing your counter each time you go in a new word, and exiting the loop when your count reaches 3, e.g.
for (; *p; p++) { /* loop over each char */
if (!in && !isspace(*p)) { /* if not in word and not space */
in = 1, n++; /* set in flag, increment words */
if (n == NWORD) /* if 3rd word, break */
break;
}
else if (isspace(*p)) /* if space */
in = 0; /* unset in flag */
}
Putting it altogether in a short example, you could do something similar to the following which takes input until the Enter key is pressed alone on an empty line, and outputting each sentence entered beginning with the third-word, or displaying the error "too few words." if a sentence with less that three words is entered, e.g.
#include <stdio.h>
#include <ctype.h>
#define MAXC 1024 /* buffer size (don't skimp) */
#define NWORD 3 /* output beginning with NWORD word */
int main (void) {
for (;;) { /* loop continually until empy-line */
char buf[MAXC] = "", /* buffer to hold line */
*p = buf; /* pointer to walk down buffer */
int n = 0, /* word counter */
in = 0; /* flag - in a word */
fputs ("\nenter sentence: ", stdout); /* prompt */
if (!fgets (buf, MAXC, stdin) || *buf == '\n') { /* read line */
puts ("all done!");
break;
}
for (; *p; p++) { /* loop over each char */
if (!in && !isspace(*p)) { /* if not in word and not space */
in = 1, n++; /* set in flag, increment words */
if (n == NWORD) /* if 3rd word, break */
break;
}
else if (isspace(*p)) /* if space */
in = 0; /* unset in flag */
}
if (n == NWORD) /* if 3 or more words */
fputs (p, stdout);
else /* other wise handle error */
fputs ("too few words.\n", stderr);
}
return 0;
}
Example Use/Output
$ ./bin/thirdword
enter sentence: one two three four five
three four five
enter sentence: one two
too few words.
enter sentence: one two three
three
enter sentence:
all done!
Look things over and let me know if you have further questions.
count = 1; //shouldn't this be count = 0? and point = &arr[i + 2]; //shouldn't this be [i+1]?
The following answers both questions.
count count count count
0 1 2 3
one two three four
i+0 i+1 i+2 i+3
point = &arr[i + 2]; along with printf("%s\n", point); says that print all characters from address of arr[i + 2] till seeing \0 character
this is my first post in this forum so please be patient.
I need to make a short programm, where the user can enter 2 strings which should be attached afterwards.
I already got this code below (I am not allowed to use other "includes").
What I need to know is: How can I deny any spaces which the user will enter?
Example: 1. String "Hello " | 2. String "World" Result should be "HelloWorld" instead of "Hello World".
#include <stdio.h>
void main()
{
char eingabe1[100];
char eingabe2[100];
int i = 0;
int j = 0;
printf("Gib zwei Wörter ein, die aneinander angehängt werden sollen\n");
printf("1. Zeichenkette: ");
gets(eingabe1);
printf("\n");
printf("2. Zeichenkette: ");
gets(eingabe2);
printf("\n");
while (eingabe1[i] != '\0')
{
i++;
}
while (eingabe2[j] != '\0')
{
eingabe1[i++] = eingabe2[j++];
}
eingabe1[i] = '\0';
printf("Nach Verketten: ");
puts(eingabe1);
}
You have to filter out the spaces as you copy your strings.
You have two string indices, i for the first string and and j for the second string. You could make better use of these indices if you used i for the reading position (of both strings subsequently; you can "reuse" loop counters in independent loops) and j for the writing position.
Here's how. Note that the code attempts to prevent buffer overflow by only adding characters if there is space in the string. This check needs only to be done when copying the second string, because j <= i when you process the first string.
#include <stdio.h>
int main()
{
char str1[100] = "The quick brown fox jumps over ";
char str2[100] = "my big sphinx of quartz";
int i = 0;
int j = 0;
while (str1[i] != '\0') {
if (str1[i] != ' ') str1[j++] = str1[i];
i++;
}
i = 0;
while (str2[i] != '\0') {
if (str2[i] != ' ' && j + 1 < sizeof(str1)) str1[j++] = str2[i];
i++;
}
str1[j] = '\0';
printf("'%s'\n", str1);
return 0;
}
In addition to avoiding spaces between your two words, you also have to avoid the newline ('\n') character placed in the input buffer by the user pressing Enter. You can do that with a simple test after you have read the line with fgets() NOT gets(). gets() is no longer part of the standard C library and should not be used due to insecurity reasons. Plus fgets provides simple length control over the number of characters a user may enter at any time.
Below, you run into trouble when you read eingabe1. After the read, eingabe1 contains a '\n' character at its end. (as it would using any of the line-oriented input functions (e.g. getline(), fgets(), etc) To handle the newline, you can simply compare its length minus '1' after you loop over the string to find the nul character. e.g.:
if (eingabe1[i-1] == '\n') i--; /* remove trailing '\n', update i */
By simply reducing the index 'i', this will guarantee that the concatenation with eingabe2 will not have any spaces or newline characters between the words.
Putting the pieces together, and using fgets in place of the insecure gets, after #define MAX 100'ing a constant to prevent hardcoding your array indexes, you could come up with something similar to:
#include <stdio.h>
#define MAX 100
int main (void)
{
char eingabe1[MAX] = {0};
char eingabe2[MAX] = {0};
int i = 0;
int j = 0;
printf("Gib zwei Wörter ein, die aneinander angehängt werden sollen\n");
printf("1. Zeichenkette: ");
/* do NOT use gets - it is no longer part of the C library */
fgets(eingabe1, MAX, stdin);
putchar ('\n');
printf("2. Zeichenkette: ");
/* do NOT use gets - it is no longer part of the C library */
fgets(eingabe2, MAX, stdin);
putchar ('\n');
while (eingabe1[i]) i++; /* set i (index) to terminating nul */
if (i > 0) {
if (eingabe1[i-1] == '\n') i--; /* remove trailing '\n' */
while (i && eingabe1[i-1] == ' ') /* remove trailing ' ' */
i--;
}
while (eingabe2[j]) { /* concatenate string - no spaces */
eingabe1[i++] = eingabe2[j++];
}
eingabe1[i] = 0; /* nul-terminate eingabe1 */
printf("Nach Verketten: %s\n", eingabe1);
return 0;
}
Output
$ ./bin/strcatsimple
Gib zwei Wörter ein, die aneinander angehängt werden sollen
1. Zeichenkette: Lars
2. Zeichenkette: Kenitsche
Nach Verketten: LarsKenitsche
Let me know if you have any further questions. I have highlighted the changes with comments above.
/**
return: the new len of the string;
*/
int removeChar(char* string, char c) {
int i, j;
int len = strlen(string)+1; // +1 to include '\0'
for(i = 0, j = 0 ; i < len ; i++){
if( string[i] == c )
continue; // avoid incrementing j and copying c
string[ j ] = string[ i ]; // shift characters
j++;
}
return j-1; // do not count '\0';
}
int main(){
char str1[] = "sky is flat ";
char str2[100] = "earth is small ";
strcat( str2, str1 );
printf("with spaces:\n\t'%s'\n", str2) ;
removeChar(str2, ' ');
printf("without spaces:\n\t'%s'\n", str2 );
}
/**
BONUS: this will remove many characters at once, eg "\n \r\t"
return: the new len of the string;
*/
int removeChars(char* string, char *chars) {
int i, j;
int len = strlen(string);
for(i = 0, j = 0 ; i < len ; i++){
if( strchr(chars,string[i]) )
continue; // avoid incrementing j and copying c
string[ j ] = string[ i ]; // shift characters
j++;
}
string[ j ]=0;
return j;
}
Thank you everyone for all the answers.
I got the solution now.
I read some advices from you and will try to remember for the future.
See the code below:
(Excuse me for the strange names for the variables, I use german words)
A few notices:
I am not allowed to use library functions
I am not allowed to use fgets for some reasons as a trainee
#include <stdio.h>
void main()
{
char eingabe1[100];
char eingabe2[100];
int i = 0;
int j = 0;
printf("gib zwei wörter ein, die aneinander angehängt werden sollen\n");
printf("1. zeichenkette: ");
gets(eingabe1);
printf("\n");
printf("2. zeichenkette: ");
gets(eingabe2);
printf("\n");
//Attach Strings
while (eingabe1[i] != '\0')
{
i++;
}
while (eingabe2[j] != '\0')
{
eingabe1[i++] = eingabe2[j++];
}
//Remove Space
eingabe1[i] = '\0';
i = 0;
j = 0;
while (eingabe1[i] != '\0')
{
if (eingabe1[i] != 32)
{
eingabe2[j++] = eingabe1[i];
}
i++;
}
eingabe2[j] = '\0';
printf("Nach verketten: ");
puts(eingabe2);
}
Sounds like homework to me.
I just wanted to mention that you probably shouldn't use sizeof() on strings these days because there may be multibyte characters in there. Use strlen() instead. The only time sizeof() would be appropriate is if you're going to malloc() a certain number of bytes to store it.
I write little loops fairly often to do low level text stuff one character at a time, just be aware that strings in C usually have a 0 byte at the end. You have to expect to encounter one and be sure you put one on the output. Space is 0x20 or decimal 32 or ' ', it's just another character.
I need to read in a file from C, store it in an array and print its contents. For some reason I keep seeing octal in my output near the end. I am dynamically creating the array after counting how many lines and characters are in it after opening the file.
output:
Abies
abies
abietate
abietene
abietic
abietin
\320ѿ_\377Abietineae --> umlaut? where did he come from?
y\300_\377abietineous
code:
int main(int argc, char ** argv) {
char c = '\0';
FILE * file;
int i = 0, j = 0, max_line = 0, max_char_per_line = 0;
/* get array limits */
file = fopen(argv[1], "r");
while ((c = fgetc(file)) != EOF){
if (c == '\n'){
max_line++; j++;
if (j > max_char_per_line){
max_char_per_line = j;
}
j = 0;
continue;
}
j++;
}
rewind(file);
/* declare array dynamically based on max line and max char */
char word[max_line][max_char_per_line];
/*read in file*/
j = 0; c = '\0';
while ((c = fgetc(file)) != EOF){
if (c == '\n'){
word[i][j] = '\0';
i++; j=0;
continue;
}
word[i][j] = c;
j++;
}
word[i][j] = '\0';
fclose(file);
for (i = 0; i < max_line; i++){
printf("%s\n", word[i]);
}
return 0;
}
Change read routine:
if (c == '\n'){
word[i][j] = 0x0;
i++; j=0;
continue;
}
and add the "\n" back in the printf routine.
for (i = 0; i < max_line; i++){
printf("%s\n", word[i]);
}
C strings are zero-terminated, not "\n"-terminated, so when you printf()ed them, printf() did not know where to stop printing.
You aren't terminating your strings. You need to add the null-terminator: \0, after the last character for each line.
In your first loop, you determine enough space for the longest line, including a newline character.
If you want to keep the newlines in your input array, just add 1 to max_char_per_line, and add the null-terminator after the newline character when you finish each line in your second loop.
If you don't need the newline in your input array, instead simply use that space for the null-terminator.
Not that it explains exactly the phenomenon you observe, but it may. You do not seem to take into account the terminating zero byte when calculating array boundaries. Just ++ the max_char_per_line after doing the calculations. And don't forget to add this zero byte if the array isn't guaranteed to be zero-initialized.
edit: do you see these lines after the output or in one these lines of output?