fgets instead of gets - c

I made a program to count a given word in a sentence. When i try to run it through the compiler it say ''gets is deprecated''. So i replaced gets with fgets, but it will output 0 with every word and sentence. How can i fix this?
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
void gotoNextWord();
char str[1026],w[1026];
int slen, wlen, wcount, i, j;
int main()
{
fgets(str, 1026, stdin);
fgets(w, 1026, stdin);
slen = strlen(str);
wlen = strlen(w);
i = j = wcount = 0;
if(w[i] == 0)
{
printf("Please specify a program argument.\n");
}
else
{
while(i<slen)
{
if (str[i] == w[0])
{
for(j=0; j<wlen; j++)
{
if(str[i+j] != w[j])
{
gotoNextWord();
break;
}
}
if(j == wlen)
{
if(isspace(str[i+j])||ispunct(str[i+j])||str[i+j]=='\0')
{
wcount++;
i += j;
}
}
}
else
{
gotoNextWord();
}
i++;
}
printf("%d\n",wcount);
}
return 0;
}
void gotoNextWord()
{
while(isspace(str[i]) == 0 && ispunct(str[i] == 0 && str[i] != '\0'))
i++;
}

A difference between gets() and fgets() is that fgets() retains a final '\n' and gets() does not.
The "word" code is now looking for contains a '\n'.
Drop the potential '\n' and continue with previous code.
fgets(str, 1026, stdin);
str[strcspn(str, "\n")] = '\0';
fgets(w, 1026, stdin);
w[strcspn(w, "\n")] = '\0';
Recommend style/code change when using fgets()
// fgets(str, 1026, stdin);
if (fgets(str, sizeof str, stdin) == NULL) return -1;

Related

Possible stack smashing?

I was looking at the following example in a book, and it seems to me that it will cause stack smashing:
int read_line(char str[], int n)
{
int ch, i = 0;
while ((ch = getchar()) != '\n')
if (i < n)
str[i++] = ch;
str[i] = '\0';
return i;
}
If I pass it an array with room for 10 chars, and n = 10, the if-statement will be true up to and including 9, and i will be incremented to 10.
Then, it will write the '\0' character at str[10] which would be just past the end of the array?
It works just fine, though (tried building with gcc on Linux, clang on Mac and VS on Windows).
VS on Windows is the only one showing an error when running the program, even though I have tried setting -fstack-protector in e.g. clang.
Your assessment is correct, the code has undefined behavior if the user types n or more bytes before the newline. There is also a problem if the end of file is encountered before the end of the line: the function will then run an infinite loop.
Here is a corrected version:
#include <stdio.h>
int read_line(char str[], int n) {
int ch, i = 0;
while ((ch = getchar()) != EOF && ch != '\n') {
if (i + 1 < n)
str[i] = ch;
i++;
}
if (n > 0) {
str[i < n ? i : n - 1] = '\0';
}
if (i == 0 && ch == EOF) {
/* end of file: no input */
return -1;
} else {
/* return the complete line length, excluding the newline */
return i;
}
}
int main() {
char buf[50];
int count = read_line(buf, sizeof buf);
if (count < 0) {
printf("Empty file\n");
} else
if (count >= sizeof buf) {
printf("Line was truncated: %s\n", buf);
} else {
printf("Read %d bytes: %s\n", count, buf);
}
return 0;
}

How to parse bigger amount of words?

I have a program, which receives filename as an input, saves file contents into 2d char array and then outputs words. It works absolutely fine for about 400 words, but then, when I add more words, it crashes. Debugging showed that i am trying to access unused address, and I don't understand how is that possible considering that previous tests with lesser amount of words were successful.
The question is: what am i missing here?
FILE: functions.c
#include "Lab10.h"
#include <stdio.h>
#include <malloc.h>
char** parser(char* filename) {
FILE* fp;
fp = fopen(filename, "r");
char** str = (char**)calloc(N, sizeof(char*) * N);
if (!str)
{
printf("\n Allocation error");
return NULL;
}
char ch;
int space = 0, words = 0;
for (int i = 0; !feof(fp); i++) // Memory allocation
{
ch = fgetc(fp);
if (!is_ch(ch))
{
if (i != space)
{
if (!(str[words] = (char*)calloc(i - space, sizeof(char) * (i - space))))
{
printf("\n Allocation error");
return NULL;
}
words++;
}
while (!is_ch(ch) && !feof(fp))
{
ch = fgetc(fp);
i++;
}
if(!feof(fp))
fseek(fp, -(int)sizeof(char), 1);
i--;
space = i;
}
}
fseek(fp, 0, SEEK_SET);
for (int i = 0; i < words; i++) // Copying words into 2d array
{
while (!is_ch(fgetc(fp)));
if (!feof(fp))
fseek(fp, -(int)sizeof(char), 1);
int j = 0;
do {
if (((fscanf(fp, "%c", &str[i][j])) != 1))
break;
j++;
} while (is_ch(str[i][j-1]) && !feof(fp));
}
return str;
}
int is_ch(char ch)
{
return ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'));
}
FILE: main.c
#define _CRT_SECURE_NO_WARNINGS
#include "Lab10.h"
#include <stdio.h>
#include <stdlib.h>
int main() {
char* filename = (char*)malloc(sizeof(char) * N);
if (!scanf("%s", filename) || filename == 0)
{
printf("\n Incorrect filename input");
return -1;
}
char** str = parser(filename);
printf("\n Contents of .txt file:");
for (int i = 0; str[i] != NULL; i++) {
printf("\n\t%d) ", i+1);
for (int j = 0; is_ch(str[i][j]); j++) {
printf("%c", str[i][j]);
}
}
return 0;
}
This answer was posted as a reply to one of the comments below the question itself. I tried writing readWord function, which recieves filepointer, reads one word and then returns pointer to the resulting array - that's eases the procedure, making it less complex. It works almost like fgets(), but it reads till non-character, instead of a newline
readWord function itself:
char* readWord(FILE* fp) {
char ch = 0;
while (!is_ch(ch))
{
ch = fgetc(fp);
if (ch == EOF || !ch)
return NULL;
}
int size = 1;
while (is_ch(ch))
{
if ((ch = fgetc(fp)) == EOF || !ch)
break;
size++;
}
fseek(fp, -(size * (int)sizeof(char)), 1);
if (ch != EOF || !ch)
size--;
char* word = (char*)calloc(size, sizeof(char) * size + 1);
if (!word)
{
printf("\n Allocation error.");
return NULL;
}
for (int i = 0; i < size; i++)
word[i] = fgetc(fp);
word[size] = '\0';
return word;
}
That's how i use it in main():
FILE* fp = fopen("test.txt", "r");
char* word;
while ((word = readWord(fp)) != NULL)
{
for (int i = 0; word[i] != '\0'; i++)
printf("%c", word[i]);
printf(" ");
}
Is there is anything i need to improve here? It works fine, but is it possible to somehow make it better?

how do i remove the similar word in strings?

I have program to remove the similar words from string but this program only removing at once word not a repeating words.
For example input:
sabunkerasmaskera kera
and should an output:
sabunmas
This my code:
#include <stdio.h>
#include <string.h>
void remove(char x[100], char y[100][100], char words[100]) {
int i = 0, j = 0, k = 0;
for (i = 0; x[i] != '\0'; i++) {
if (x[i] == ' ') {
y[k][j] = '\0';
k++;
j = 0;
} else {
y[k][j] = x[i];
j++;
}
}
y[k][j] = '\0';
j = 0;
for (i = 0; i < k + 1; i++) {
if (strcmp(y[i], kata) == 0) {
y[i][j] = '\0';
}
}
j = 0;
for (i = 0; i < k + 1; i++) {
if (y[i][j] == '\0')
continue;
else
printf("%s ", y[i]);
}
printf ("\n");
}
int main() {
char x[100], y[100][100], kata[100];
printf ("Enter word:\n");
gets(x);
printf("Enter word to remove:\n");
gets(words);
remove(x, y, words);
return 0;
}
My program output its:
sabunkerasmaskerara
and that should not be the case. Maybe I need your opinion to fixed this program and also I need help to make it better.
Your solution does not work because it uses strcmp to compare the string portions, which only works if the substring is at the end of the string, as this makes it null-terminated.
You should instead use strstr to locate the matches and use memmove to shift the string contents.
There are other issues in your code:
do not use gets()
y is unnecessary for this task.
words is not defined
Here is a modified version:
#include <stdio.h>
#include <string.h>
char *remove_all(char *str, const char *word) {
size_t len = strlen(word);
if (len != 0) {
char *p = str;
while ((p = strstr(p, word)) != NULL) {
memmove(p, p + len, strlen(p + len) + 1);
}
}
return str;
}
int main() {
char str[100], word[100];
printf ("Enter string:\n");
if (!fgets(str, sizeof str, stdin))
return 1;
printf("Enter word to remove:\n");
if (!fgets(word, sizeof word, stdin))
return 1;
word[strcspn(word, "\n")] = '\0'; // strip the trailing newline if any
remove_all(str, word);
fputs(str, stdout);
return 0;
}

C, comparing arrays, find a character,

So I am kinda new to coding, but what I want to do is write a string, write one character I wish not to be in the string if it occurs. I've tried using removedChar = getchar() instead of fgets(removedChar, 2, stdin); but then I can't do the != in the if statement.
I would really appreciate your help.
int main() {
char str[20], removedChar[2];
int i, n, j;
printf("ENTER A STRING:");
fgets(str, 20, stdin);
printf("ENTER WHAT CHAR YOU WISH TO REMOVE: ");
fgets(removedChar, 2, stdin);
n = strlen(str);
for (i = 0, j = 0; i < n; i++) {
if (strcmp(str, removedChar) == 0) {
str[j] = str[i];
j++;
}
if (str[i] == ' ') {
str[j] = str[i];
j++;
}
}
str[j] = '\0';
printf("string after removing character = %s", str);
system("pause");
return 0;
}
Firstly this line:
if (strcmp(str, removedChar) == 0)
is comparing if two strings are identical. Please see strcmp.
You need to instead compare character against characters, instead of equality of string against string.
Having said this, you can now just simply loop over the string, and use != to rule out matching characters, and update the string accordingly with a counter.
Additionally, it is always safe to check the return value of fgets, and also check that you havn't exceeded the buffer length.
This is the code that uses these ideas:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define STRSIZE 20
int
main(int argc, const char *argv[]) {
char str[STRSIZE];
int i, j, removedchar;
size_t slen;
printf("Enter a string: ");
if (fgets(str, STRSIZE, stdin) == NULL) {
printf("Error reading string\n");
return 1;
}
slen = strlen(str);
if (slen > 0) {
if (str[slen-1] == '\n') {
str[slen-1] = '\0';
} else {
printf("Error: Exceeded Buffer length of %d.\n", STRSIZE);
return 1;
}
}
if(!*str) {
printf("Error: No string entered.\n");
return 1;
}
printf("Enter what character you wish to remove: ");
removedchar = getchar();
if (removedchar == '\n') {
removedchar = ' ';
printf("No character was entered. Spaces will be removed if found\n");
}
j = 0;
for (i = 0; str[i] != '\0'; i++) {
if (str[i] != removedchar) {
str[j++] = str[i];
}
}
str[j] = '\0';
printf("Changed String = %s\n", str);
return 0;
}
You should use str[i] != removedChar[0] instead of using strcmp() which compares the full strings.
Also note that you should strip the newline character from the string read by fgets().
Here is a corrected version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char str[80], removedChar[80];
int i, n, j;
printf("ENTER A STRING: ");
if (!fgets(str, sizeof str, stdin))
return 1;
str[strcspn(str, "\n")] = '\0'; // strip the newline character if present
printf("ENTER WHAT CHAR YOU WISH TO REMOVE: ");
if (!fgets(removedChar, sizeof removedChar, stdin))
return 1;
for (i = 0, j = 0; str[i] != '\0'; i++) {
if (str[i] != removedChar[0]) {
str[j] = str[i];
j++;
}
}
str[j] = '\0';
printf("string after removing character = %s\n", str);
system("pause");
return 0;
}

Better Way Without Goto?

I have a program where my code uses a goto statement, and I want to get rid of it in a nice way, but I can't seem to find a solution. If goto is the best way, then please let me know. Here is a summary of the code:
//Counts how many times every word appears in a file
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define NUMWORDS 1000
#define WORDLEN 50
typedef struct
{
char word[WORDLEN + 1];
int num;
} Appearance;
int main(int argc, char *argv[]) {
FILE *readfile;
Appearance *appearlist[NUMWORDS] = {NULL};
char word[WORDLEN + 1];
int i;
//Get a valid filename and open the file, store pointer into readfile
...
char c;
while (c != EOF) {
skip: //Annoying label
//Get a word from readfile, store into word
...
if (word[0] != '\0') {
for (i = 0; i < NUMWORDS && appearlist[i]; i++) {
if (strcmp(appearlist[i] -> word, word) == 0) {
appearlist[i] -> num++;
goto skip; //Annoying goto
}
}
appearlist[i] = (Appearance *) malloc(sizeof(Appearance));
appearlist[i] -> num = 1;
strcpy(appearlist[i] -> word, word);
}
}
//Display results, free memory
...
return 0;
}
The problem is, I want to skip code that is outside of the loop I want to skip from. I would like to not create another variable only designed for this. If you want the full code, click on "Show code snippet."
//Counts how many times every word appears in a file
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define NUMWORDS 1000
#define WORDLEN 50
#define FILENAMELEN 50
typedef struct
{
char word[WORDLEN + 1];
int num;
} Appearance;
int main(int argc, char *argv[])
{
char filename[FILENAMELEN];
FILE *readfile;
Appearance *appearlist[NUMWORDS] = {NULL};
char word[WORDLEN + 1];
size_t ln;
int i;
if (argc == 2)
strncpy(filename, argv[1], sizeof(filename));
else {
printf("Enter a filename to count appearances from, or just press enter to quit: ");
fgets(filename, FILENAMELEN, stdin);
ln = strlen(filename) - 1;
if (filename[ln] == '\n')
filename[ln] = '\0';
}
while((readfile = fopen(filename, "r")) == NULL) {
if (filename[0] == '\0')
return 0;
printf("Invalid file! Please enter another filename, or just press enter to quit: ");
fgets(filename, FILENAMELEN, stdin);
ln = strlen(filename) - 1;
if (filename[ln] == '\n') filename[ln] = '\0';
}
char c;
while (c != EOF) {
skip:
for (i = 0; (c = getc(readfile)) != EOF && (isalnum(c) || c == '\''); i++) {
if (i >= WORDLEN) {
word[i] = '\0';
printf("\nWarning: word too long (over %d characters), trimming to: %s\n", WORDLEN, word);
while ((c = getc(readfile)) != EOF && (isalnum(c) || c == '\'')) ;
} else {
word[i] = tolower(c);
}
}
word[i] = '\0';
if (word[0] != '\0') {
for (i = 0; i < NUMWORDS && appearlist[i]; i++) {
if (strcmp(appearlist[i] -> word, word) == 0) {
appearlist[i] -> num++;
goto skip;
}
}
appearlist[i] = (Appearance *) malloc(sizeof(Appearance));
appearlist[i] -> num = 1;
strcpy(appearlist[i] -> word, word);
}
}
for (i = 0; i < NUMWORDS && appearlist[i]; i++) {
printf("Word: %s, Appearances: %d\n", appearlist[i] -> word, appearlist[i] -> num);
free(appearlist[i]);
}
return 0;
}
Using goto in this case is often considered acceptable.
Alternatives would be to set a variable so that you can continue in the outer loop after breaking from the inner one, or turning the whole segment that you want to escape from into a separate function, and returning from it instead of using goto.
I'm ignoring any other issues there may be with the code that aren't relevant to the question!
Put everything beginning with the 'if' statement into a separate method (let's call it "process" and replace the goto with return. Then the while-loop becomes:
while (c != EOF) {
//Get a word from readfile, store into word
...
process(...);
}
Sometimes using goto is a hint that code should use a helper function
static bool findword(Appearance *appearlist, size_t size, const char *word) {
for (size_t i = 0; i < size && appearlist[i]; i++) {
if (strcmp(appearlist[i]->word, word) == 0) {
appearlist[i]->num++;
return true;
}
}
return false;
}
while (c != EOF) {
//Get a word from readfile, store into word
...
if (word[0] != '\0') {
if (findword(appearlist, NUMWORDS, word)) {
continue;
}
appearlist[i] = (Appearance *) malloc(sizeof(Appearance));
appearlist[i] -> num = 1;
strcpy(appearlist[i] -> word, word);
}
}

Resources