This question already has answers here:
strcmp on a line read with fgets
(6 answers)
Closed 3 years ago.
I am reading words from file & need to search some specific words, below is my code
string read = malloc(50 * sizeof(char));
FILE* p = fopen("word","r");
while(fgets(read,50,p))
{
printf("%s\n",read);
if(strcmp(read,"apple") == 0)
{
printf("apple found\n");
break;
}
}
And sample file 'word' is as below,
$: more word
liol
apple
scizzors
big
bang
mentalist
scapegrace
goat
goti
Why is strcmp not working in this case, printf can print string read, so char pointer is working fine.
The fgets() function, in most circumstances, retains the newline at the end of the line. Hence yourtext\n will not compare equal to yourtext. You would have noted this with one of my favorite tricks for checking strings:
printf ("[%s]\n", read);
The presence of a newline before the closing ] would have immediately alerted you to the problem, or at a minimum raised an eyebrow.
If you want to strip a newline off before comparing, you can do something like:
int ln = strlen (read);
if ((ln > 0) && (read[ln-1] == '\n'))
read[ln-1] = '\0';
Alternatively, you could skip that and just do:
if (strcmp (read,"apple\n") == 0)
It's not necessarily elegant but, if you don't need to use the word for anything other than that comparison, it'll be just fine.
However, you may then need to worry about the last line in the file in case it has no newline at the end of it so maybe it's better to use the newline-removing code above.
To search for a specific string in the line, rather than match the entire line, strncmp (which searches for n characters) can be used.
if (strncmp(read,"apple",strlen("apple")) == 0)
{
printf("apple found\n");
break;
}
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
So I'm trying to make it so that you can write text into a file until you make a newline or type -1. My problem is that when you write, it just keeps going until it crashes and gives the error "Stack around the variable "inputChoice" was corrupted".
I believe the problem is that the program doesn't stop accepting stdin when you want to stop typing (-1, newline) and that causes the error. I've tried with a simple scanf and it works, but you can only write a word. No spaces and it doesn't support multiple lines either. That's why I have to use fgets
Judging from your comments, I assume that there are some basic concepts in C
that you haven't fully understood, yet.
C-Strings
A C-String is a sequence of bytes. This sequence must end with the value 0.
Every value in the sequence represents a character based on the
ASCII encoding, for example the
character 'a' is 97, 'b' is 98, etc. The character '\0' has
the value 0 and it's the character that determines the end of the string.
That's why you hear a lot that C-Strings are '\0'-terminated.
In C you use an array of chars (char string[], char string[SOME VALUE]) to
save a string. For a string of length n, you need an array of dimension n+1, because
you also need one space for the terminating '\0' character.
When dealing with strings, you always have to think about the proper type,
whether your are using an array or a pointer. A pointer
to char doesn't necessarily mean that you are dealing with a C-String!
Why am I telling you this? Because of:
char inputChoice = 0;
printf("Do you wish to save the Input? (Y/N)\n");
scanf("%s", &inputChoice);
I haven't changed much, got very demotivated after trying for a while.
I changed the %s to an %c at scanf(" %c, &inputChoice) and that
seems to have stopped the program from crashing.
which shows that haven't understood the difference between %s and %c.
The %c conversion specifier character tells scanf that it must match a single character and it expects a pointer to char.
man scanf
c
Matches a sequence of characters whose length is specified by the maximum field
width (default 1); the next pointer must be a
pointer to char, and there must be enough room for all the characters
(no terminating null byte is added). The usual skip of
leading white space is suppressed. To skip white space first, use an explicit space in the format.
Forget the bit about the length, it's not important right now.
The important part is in bold. For the format scanf("%c", the function
expects a pointer to char and its not going to write the terminating '\0'
character, it won't be a C-String. If you want to read one letter and one
letter only:
char c;
scanf("%c", &c);
// also possible, but only the first char
// will have a defined value
char c[10];
scanf("%c", c);
The first one is easy to understand. The second one is more interesting: Here
you have an array of char of dimension 10 (i.e it holds 10 chars). scanf
will match a single letter and write it on c[0]. However the result won't be
a C-String, you cannot pass it to puts nor to other functions that expect
C-Strings (like strcpy).
The %s conversion specifier character tells scanf that it must match a sequence of non-white-space characters
man scanf
s
Matches a sequence of non-white-space characters; the next pointer must be a
pointer to the initial element of a character array that is long enough to
hold the input sequence and the terminating null byte ('\0'), which is added
automatically.
Here the result will be that a C-String is saved. You also have to have enough
space to save the string:
char string[10];
scanf("%s", string);
If the strings matches 9 or less characters, everything will be fine, because
for a string of length 9 requires 10 spaces (never forget the terminating
'\0'). If the string matches more than 9 characters, you won't have enough
space in the buffer and a buffer overflow (accessing beyond the size) occurs.
This is an undefined behaviour and anything can happen: your program might
crash, your program might not crash but overwrites another variable and thus
scrwes the flow of your program, it could even kill a kitten somewhere, do
you really want to kill kittens?
So, do you see why your code is wrong?
char inputChoice = 0;
scanf("%s", &inputChoice);
inputChoice is a char variable, it can only hold 1 value.
&inputChoice gives you the address of the inputChoice variable, but the
char after that is out of bound, if you read/write it, you will have an
overflow, thus you kill a kitten. Even if you enter only 1 character, it will
write at least 2 bytes and because you it only has space for one character, a kitten will die.
So, let's talk about your code.
From the perspective of an user: Why would I want to enter lines of text, possibly a lot of lines of text
and then answer "No, I don't want to save the lines". It doesn't make sense to
me.
In my opinion you should first ask the user whether he/she wants to save the
input first, and then ask for the input. If the user doesn't want to save
anything, then there is no point in asking the user to enter anything at
all. But that's just my opinion.
If you really want to stick to your plan, then you have to save every line and
when the user ends entering data, you ask and you save the file.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFERLEN 1024
void printFile () {
int i;
char openFile[BUFFERLEN];
FILE *file;
printf("What file do you wish to write in?\n");
scanf("%s", openFile);
getchar();
file = fopen(openFile, "w");
if (file == NULL) {
printf("Could not open file.\n");
return;
}
// we save here all lines to be saved
char **lines = NULL;
int num_of_lines = 0;
char buffer[BUFFERLEN];
printf("Enter an empty line of -1 to end input\n");
// for simplicity, we assume that no line will be
// larger than BUFFERLEN - 1 chars
while(fgets(buffer, sizeof buffer, stdin))
{
// we should check if the last character is \n,
// if not, buffer was not large enough for the line
// or the stream closed. For simplicity, I will ignore
// these cases
int len = strlen(buffer);
if(buffer[len - 1] == '\n')
buffer[len - 1] = '\0';
if(strcmp(buffer, "") == 0 || strcmp(buffer, "-1") == 0)
break; // either an empty line or user entered "-1"
char *line = strdup(buffer);
if(line == NULL)
break; // if no more memory
// process all lines that already have been entered
char **tmp = realloc(lines, (num_of_lines+1) * sizeof *tmp);
if(tmp == NULL)
{
free(line);
break; // same reason as for strdup failing
}
lines = tmp;
lines[num_of_lines++] = line; // save the line and increase num_of_lines
}
char inputChoice = 0;
printf("Do you wish to save the Input? (Y/N)\n");
scanf("%c", &inputChoice);
getchar();
if (inputChoice == 'Y' || inputChoice == 'y') {
for(i = 0; i < num_of_lines; ++i)
fprintf(file, "%s\n", lines[i]); // writing every line
printf("Your file has been saved\n");
printf("Please press any key to continue");
getchar();
}
// closing FILE buffer
fclose(file);
// free memory
if(num_of_lines)
{
for(i = 0; i < num_of_lines; ++i)
free(lines[i]);
free(lines);
}
}
int main(void)
{
printFile();
return 0;
}
Remarks on the code
I used the same code as yours as the base for mine, so that you can spot the
differences much quicker.
I use the macro BUFFERLEN for declaring the length of the buffers. That's
my style.
Look at the fgets line:
fgets(buffer, sizeof buffer, stdin)
I use here sizeof buffer instead of 1024 or BUFFERLEN. Again, that's my
style, but I think doing this is better, because even if you change the size
of the buffer by changing the macro, or by using another explicit size, sizeof buffer
will always return the correct size. Be aware that this only works when
buffer is an array.
The function strdup returns a pointer a pointer to a new string that
duplicates the argument. It's used to create a new copy of a string. When
using this function, don't forget that you have to free the memory using
free(). strdup is not part of the standard library, it conforms
to SVr4, 4.3BSD, POSIX.1-2001. If you use Windows (I don't use Windows,
I'm not familiar with the Windows ecosystem), this function might not be
present. In that case you can write your own:
char *strdup(const char *s)
{
char *str = malloc(strlen(s) + 1);
if(str == NULL)
return NULL;
strcpy(str, s);
return str;
}
I'm trying to create a function that removes duplicate strings from a stream in C. The strings are already sorted so the only necessary step is to check the string that just appeared to make sure the current string is not a duplicate. However, my attempted implementation is not giving me the correct output.In fact, I get no output at all. The strings are separated by newline characters. Can anyone tell me what I'm missing here?
void dupEliminate(int file, char string[100])
{
FILE *stream;
stream = fdopen(file, "r");
char* savedString;
char* prevString;
while(!feof(stream)){
(fgets(savedString, 100, stream));
if(strcmp(savedString,prevString) != 0 ){
strcat(string, savedString);
strcpy(prevString,savedString);
}
char* prevString;
prevString is uninitialized in this function , and yet you compare it here -
if(strcmp(savedString,prevString) != 0 )
Also , before taking input in savedString using fgets , you need to allocate memory to it using malloc or calloc ,as it is an unintialized pointer.
What it will compare to ? Initialize you prevString and then compare it .
Note- Just a suggestion instead of using while(!feof(stream)) , use fgets to control loop -
while(fgets(savedString, 100, stream)!=NULL){
...
}
You need to give savedString and prevString some memory - malloc or make them char[101]
You will also need to initialise prevString
prevString needs to be updated with the last line unconditionally, not in the if block
I don't like the names either :)
It is unclear what you ultimately want to do with the non-duplicate strings given the code you have posted. At present, even if you collect only non-duplicate strings, you are currently overwriting the value in string without doing any thing with it.
If you are reading from a continual stream, then you either need to just print (or save or copy) the non-duplicate string somewhere to make use of it, or you need to buffer the non-duplicate strings somewhere and make use of them when your buffer is full.
Since you say you are not getting the correct output, but have no output functions listed, it is somewhat of a guess what you are trying to do. If you simply want to output the non-duplicate strings, then you can simply print them (note using fgets you will have a newline at the end of each string read). An example would be:
#define MAXC 100
...
char string[MAXC] = {0};
...
void dupEliminate (int file, char *string)
{
FILE *stream;
stream = fdopen (file, "r");
if (!stream) {
fprintf (stderr, "dupEliminate() error: stream open failed.\n");
return;
}
char pre[MAXC] = {0}; /* previous string */
size_t word = 0;
while (fgets (string, MAXC, stream)) {
if (word == 1) {
if (strcmp (pre, string) != 0) {
printf ("%s", pre);
printf ("%s", string);
}
else
printf ("%s", string);
}
if (word > 1 && strcmp (pre, string) != 0)
printf ("%s", string);
strncpy (pre, string, MAXC);
word++;
}
fclose (stream);
}
If your intent was to buffer all non-duplicate strings and make them available in main(), then there are several things you need to do. Let me know if you have any further questions and I'm happy to work with your further once I have more details about your intent.
So I have a wall of text in a file and I need to recognize some words that are between the $ sign and call them as numbers then print the modified text in another file along with what the numbers correspond to.
Also lines are not defined and columns should be max 80 characters.
Ex:
I $like$ cats.
I [1] cats.
[1] --> like
That's what I did:
#include <stdio.h>
#include <stdlib.h>
#define N 80
#define MAX 9999
int main()
{
FILE *fp;
int i=0,count=0;
char matr[MAX][N];
if((fp = fopen("text.txt","r")) == NULL){
printf("Error.");
exit(EXIT_FAILURE);
}
while((fscanf(fp,"%s",matr[i])) != EOF){
printf("%s ",matr[i]);
if(matr[i] == '\0')
printf("\n");
//I was thinking maybe to find two $ but Idk how to replace the entire word
/*
if(matr[i] == '$')
count++;
if(count == 2){
...code...
}
*/
i++;
}
fclose(fp);
return 0;
}
My problem is that fscanf doesn't recognize '\0' so it doesn't go in the next line when I print the array..also I don't know how to replace $word$ with a number.
Not only will fscanf("%s") read one whitespace-delimited string at a time, it will also eat all whitespace between those strings, including line terminators. If you want to reproduce the input whitespace in the output, as your example suggests you do, then you need a different approach.
Also lines are not defined and columns should be max 80 characters.
I take that to mean the number of lines is not known in advance, and that it is acceptable to assume that no line will contain more than 80 characters (not counting any line terminator).
When you say
My problem is that fscanf doesn't recognize '\0' so it doesn't go in the next line when I print the array
I suppose you're talking about this code:
char matr[MAX][N];
/* ... */
if(matr[i] == '\0')
Given that declaration for matr, the given condition will always evaluate to false, regardless of any other consideration. fscanf() does not factor in at all. The type of matr[i] is char[N], an array of N elements of type char. That evaluates to a pointer to the first element of the array, which pointer will never be NULL. It looks like you're trying to determine when to write a newline, but nothing remotely resembling this approach can do that.
I suggest you start by taking #Barmar's advice to read line-by-line via fgets(). That might look like so:
char line[N+2]; /* N + 2 leaves space for both newline and string terminator */
if (fgets(line, sizeof(line), fp) != NULL) {
/* one line read; handle it ... */
} else {
/* handle end-of-file or I/O error */
}
Then for each line you read, parse out the "$word$" tokens by whatever means you like, and output the needed results (everything but the $-delimited tokens verbatim; the bracket substitution number for each token). Of course, you'll need to memorialize the substitution tokens for later output. Remember to make copies of those, as the buffer will be overwritten on each read (if done as I suggest above).
fscanf() does recognize '\0', under select circumstances, but that is not the issue here.
Code needs to detect '\n'. fscanf(fp,"%s"... will not do that. The first thing "%s" directs is to consume (and not save) any leading white-space including '\n'. Read a line of text with fgets().
Simple read 1 line at a time. Then march down the buffer looking for words.
Following uses "%n" to track how far in the buffer scanning stopped.
// more room for \n \0
#define BUF_SIZE (N + 1 + 1)
char buffer[BUF_SIZE];
while (fgets(buffer, sizeof buffer, stdin) != NULL) {
char *p = buffer;
char word[sizeof buffer];
int n;
while (sscanf(p, "%s%n", word, &n) == 1) {
// do something with word
if (strcmp(word, "$zero$") == 0) fputs("0", stdout);
else if (strcmp(word, "$one$") == 0) fputs("1", stdout);
else fputs(word, stdout);
fputc(' ', stdout);
p += n;
}
fputc('\n', stdout);
}
Use fread() to read the file contents to a char[] buffer. Then iterate through this buffer and whenever you find a $ you perform a strncmp to detect with which value to replace it (keep in mind, that there is a 2nd $ at the end of the word). To replace $word$ with a number you need to either shrink or extend the buffer at the position of the word - this depends on the string size of the number in ascii format (look solutions up on google, normally you should be able to use memmove). Then you can write the number to the cave, that arose from extending the buffer (just overwrite the $word$ aswell).
Then write the buffer to the file, overwriting all its previous contents.
This question already has answers here:
Removing trailing newline character from fgets() input
(14 answers)
Closed 7 years ago.
The task is write a c program to print lines read from an input file (maybe very very large), but without '\n'. Please see the comment in the code below, is it a typical way or good way to do so??
int main() {
const char *input_wavlist_file = "/home/legend0011/Downloads/test-sample-list.txt";
const int BUFFER_SIZE = 100;
FILE *fr = fopen(input_wavlist_file, "r");
if (fr == NULL) {
printf("Error opening input wav list file!\n");
exit(1);
}
char str[BUFFER_SIZE];
while((fgets(str, BUFFER_SIZE, fr)) != NULL) {
char *pch = strchr(str, '\n');
char *filepath = str;
if (pch != NULL) *pch = '\0'; // is this a typical way????????
printf("reading==>%s",filepath);
}
fclose(fr);
}
fgets() comes with a newline character suppress the newline and print it.
size_t n = strlen(str);
if(n>0 && str[n-1] == '\n')
{
str[n-1] = '\0';
}
printf("%s",str);
The issue you're facing here is with fgets() behaviour. As per the man page,
fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the buffer....
So, it reads and stores the tralining newline into the read buffer. That is the one which is getting printed to the output. Instead of trying n_not to print_ the newline, you can simply replace the trailing \n with \0 after taking the input to get your issue solved.
As this is a simple replacement operation, without having strchr() also you can get the job done in an easy way.
Simple Algo:
Read the line using fgets().
If the return is not NULL, calculate the length of the input using strlen().
Replace the buffer[strlen-1] element (actually \n, you can double-check) with null \0.
note: The proper signature of main() is int main(void)
This answers the question in your comment. There is no string assign operator in c. You have to provide some space for this new string or destructively ammendment the original.
The simplest way to achieve what you want would be use strncpy to copy the first n characters of your source string to a new destination.
I have a program that reads student names and grades line by line from a .txt file using fgets and a buffer declared as:
char buffer[1024];
Now the file should end with the string "end" on a line all by itself.
How do I tell a while loop to terminate when buffer == "end"?
I tried using strcmp but it segfaults for some reason.
To simply answer your question, strcmp actually is the function you're looking for:
#include <string.h>
if(!strcmp(line, "end\n") || !strcmp(line, "end\r\n"))
break;
or similar should do the trick. Note the ! in front of strcmp as strcmp returns 0 (i.e. false) if the strings match. But I guess you already know that since you've already mentioned strcmp in your question.
On the segfault issue: Are you sure none of the pointers you pass to strcmp are NULL? Most C standard libraries don't do any error checking here and will run into segfaults when trying to read the memory at *(0).
Another possible issue that pops into my mind is that using strcmp will of course only work if you've already split your buffer into an array of single strings. strtok_r is probably most suited for this task (Altough quite tricky to use).
Why not just use formatted IO when the format of the text in the file is fixed and use !EOF for looping?
For example:
while(fp!=EOF)
{ fscanf(fp,"%s %s %d",name,grades, &marks);
printf("Name: %s\tGrade: %s\t Marks: %d\n",name,grade,marks);
}
This would automatically detect the end of file, which would also remove the condition of having a string 'end' in the text file.
char *fgets(char *restrict s, int n, FILE *restrict stream);
The fgets() function shall read bytes from stream into the array pointed to by s, until n-1 bytes are read, or a is read and transferred to s, or an end-of-file condition is encountered. The string is then terminated with a null byte.
so, when you read string "end", the element in buffer should be {'e', 'n', 'd', '\n', '\0'}, you can using strcmp as follows:
size_t len = strlen(buffer);
if (buffer[len - 1] == '\n')
{
buffer[len - 1] == '\0';
}
if (strcmp(buffer, "end") == 0)
{
//end
}
or
if (strncmp(buffer, "end", 3) == 0)
{
//end
}
Using strncmp(buffer, "end", 3) to compare the first three characters along with if(3 == strlen(buffer) to make sure end does not start the line, should solve the problem.