Comparing strings in C - c

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.

Related

fscanf() how to go in the next line?

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.

The best way to print lines from an inputfile without '\n'? [duplicate]

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.

strcmp not working for fgets file read input string [duplicate]

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;
}

Can't determine value of character at the end of a string

I'm new to C programming. I am trying to make a program that takes some simple input. However I found that on comparison of my input string to what the user "meant" to input, there is an additional character at the end. I thought this might be a '\0' or a '\r' but that seems not to be the case. This is my snippet of code:
char* getUserInput(char* command, char $MYPATH[])
{
printf("myshell$ ");
fgets(command, 200, stdin);
printf("%u\n", (unsigned)strlen(command));
if ((command[(unsigned)strlen(command) - 1] == '\0') || (command[(unsigned)strlen(command) - 1] == '\r'))
{
printf("bye\n");
}
return command;
}
The code shows that when entering, say "exit" that 5 characters are entered. However I can't seem to figure out the identity of this last one. "Bye" never prints. Does anyone know what this mystery character could be?
The magical 5th element most probably is a newline character: \n
From man fgets() (emphasis by me):
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. A '\0' is
stored after the last character in the buffer.
To prove this print out each character read by doing so:
char* getUserInput(char* command, char $MYPATH[])
{
printf("myshell$ ");
fgets(command, 200, stdin);
printf("%u\n", (unsigned)strlen(command));
{
size_t i = 0, len = strlen(command);
for (;i < len; ++i)
{
fprintf(stderr, "command[%zu]='%c' (%hhd or 0x%hhx)\n", i, command[i], command[i], command[i]);
}
}
...
assumptions
array indexes in c are started with 0
strlen returns length of string
so, if you have string "exit", this will be 5 symbols in array = e, x, i, t, \0, strlen return 4, but you're trying to decrement it by 1, so you're checking last symbol in string, instead on NULL terminator
to check NULL terminator use command[strlen(command)] - this will give you \0 always, so there is no sense in it
if you want to compare strings use strcmp function
UPDATE: issue with your program is because fgets appends \n symbol at then end of string:
A newline character makes fgets stop reading, but it is considered a
valid character by the function and included in the string copied to
str.
The reason you don't see the last char is because strlen() won't calculate '\0' into the string's length. So testing for '\0' wont succeed.
for instance, const char* a = "abc"; then strlen(a) will be 3. if you want to test it, you need to access it by command[strlen(command)]
The reason for getting strlen equals to 5 on "exit" is because fgets will append the '\n' character at the end of the input. You could test it by command[strlen(command) -1 ] == '\n'

In C how to strcmp just the beginning 2 characters and then concatenate? [duplicate]

This question already has answers here:
How to check if a string starts with another string in C?
(10 answers)
Closed 7 years ago.
In C how do I strcmp just the beginning 2 characters? Then concatenate with another string? Something like this:
char s[10];
scanf("%s",s);
/* if i input "cs332" or "cs234", anything start with cs */
if (strcmp("cs",???)==0)
strcat(s,"by professor");
You are looking for the strncmp function which is functionally identical to strcmp but limits the number of characters checked. So you would use it with a length of two and the comparison string of "cs". But, you have a few other problems here.
First, your buffer is not big enough. There is no string that will fit into a ten-character buffer when you append the text "by professor" to it.
Secondly, robust code will never use scanf with an unbounded-string format specifier: that's asking for a buffer overflow problem. The scanf family is meant for formatted input and there is little more unformatted than user input :-)
If you want a robust input solution, see one of my previous answers.
Thirdly, you should always assume that concatenating a string may overflow your buffer, and introduce code to prevent this. You need to add up:
the current length of the string, input by the user.
the length of the appending string ("by professor").
one more for the null terminator.
and ensure the buffer is big enough.
The method I would use would be to have a (for example) 200-byte buffer, use getLine() from the linked answer (reproduced below to make this answer self-contained) with a sufficiently smaller size (say 100), then you can be assured that appending "by professor" will not overflow the buffer.
Function:
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
Test code:
// Test program for getLine().
int main (void) {
int rc;
char buff[10];
rc = getLine ("Enter string> ", buff, sizeof(buff));
if (rc == NO_INPUT) {
// Extra NL since my system doesn't output that on EOF.
printf ("\nNo input\n");
return 1;
}
if (rc == TOO_LONG) {
printf ("Input too long [%s]\n", buff);
return 1;
}
printf ("OK [%s]\n", buff);
return 0;
}
why not directly comparing characters rather than calling strcmp?
E.g.
if(s[0]=='c' && s[1]=='s'){
...
}
if (strncmp("cs",???, 2)==0) strcat(s,"by professor");
Use strncmp
Several ways to do this.
String comparison:
if ( s[0] == 'c' && s[1] == 's' )
Is the naive way, as you can't expand this easily to slightly longer codes (say 3/4 characters in length).
I guess you've gathered you should be using strncmp() right?
String Concaternation
Don't use strcat. Really. If you concatenate two strings whose length is greater than the size of s (the destination) you're in trouble. Consider using snprint() instead, like this:
char str[80];
snprintf(str, 80, "%s by professor", s);
Or, you could use strncat() as Heath points out:
char s[80];
strncat(s, " by professor", 80);
You can use strncmp.
Edit:
strcat(s,"by professor");
// s is an array of 10 characters. You need to make sure s is big enough
// to hold the string that needs to be concatenated + to have a terminating
// character '\0'.
Yes, as said, strcmp is the preferred method. Here's just a different way to do the same.
#define CS 29539
char s[80];
scanf("%60s", s);
if( *(short *)s == CS )
if( strlcat(s, " by professor", sizeof(s)) >= sizeof(s) )
fprintf(stderr, "WARNING: truncation detected: %s", s);

Resources