fgets naturally puts a terminating zero in C? - c

struct DVDInfo *ReadStruct( void ) {
struct DVDInfo *infoPtr;
int num;
char line[ kMaxLineLength ];
char *result;
infoPtr = malloc( sizeof( struct DVDInfo ) );
if ( NULL == infoPtr ) {
printf( "Out of memory!!! Goodbye!\n" );
exit( 0 );
}
printf( "Enter DVD Title: " );
result = fgets( line, kMaxLineLength, stdin );
line[ strlen( line ) - 1 ] = '\0';
infoPtr->title = MallocAndCopy( line );
printf( "Enter DVD comment: " );
result = fgets( line, kMaxLineLength, stdin );
line[ strlen( line ) - 1 ] = '\0';
infoPtr->comment = MallocAndCopy( line );
do {
printf( "Enter DVD Rating (1-10): " );
scanf( "%d", &num );
Flush();
}
while ( ( num < 1 ) || ( num > 10 ) );
infoPtr->rating = num;
printf( "\n----------\n" );
return( infoPtr );
}
I asked a different question about this code in another thread on stackoverflow but didn't want to double up on that one - why is the terminating zero being added to the end of these files read in by fgets? fgets adds the terminating zero anyway, isn't this overkill?

Generally, you replace the newline character that fgets adds to the string with a NUL character. In all cases, fgets will NUL-terminate.
See: http://www.opengroup.org/onlinepubs/009695399/functions/fgets.html

fgets writes a nul terminator into the buffer you provide (if you specify the buffer size as larger than 0). Otherwise you could not call strlen() on it, strlen() expects a string, and if it isn't nul terminated it is not a string.
You're asking about
line[ strlen( line ) - 1 ] = '\0';
This strips off the last character in line .If you've read a line, it replaces the last character, presumably a \n with a nul terminator.
Consider that fgets just read a line, e.g. your line buffer now contains the string "Hello\n" (the \n is just the escape sequence here, it's actually just 1 character, not 2)
strlen ("Hello\n") is 6, and 6-1 is 5, so the 5. index is replaced by 0
"Hello\n"
^
|
Add 0 terminator
Result:
"Hello"
Just be careful:
you don't want to do line[ strlen(line) - 1 ] = '\0'; on an empty string, in that case you'll end up doing line[-1].
You should check if fgets succeds. You don't want to poke around in line if fgets failed, and didn't write anything to your buffer.
You might want to check whether a whole line actually got read. IF the line you read is larger than
kMaxLineLength ,or e.g. if the last "line" in the file doesn't have a trailing \n , strlen(line) -1 will not be a \n (newline).

Your
result = fgets( line, kMaxLineLength, stdin );
is Ok since the size of line is kMaxLineLength.
fgets reads in at most one less than size characters from stream and stores them into the buffer ...

The line[ strlen( line ) - 1 ] = '\0'; are unnecessary (and insecure — strlen() isn't going to work properly if the string isn't already nul-terminated). fgets() will nul-terminate the buffer. Also, you should be checking that result != NULL before attempting to copy line. fgets() returns NULL at end-of-file or if an error occurs.

Yes, it's overkill.
One suggestion to make it more robust against code rot... change
result = fgets( line, kMaxLineLength, stdin );
to
result = fgets( line, sizeof(line), stdin );

Related

How to print a line from its beginning?

I've been coding a program to write data into a text file and practice data processes in c, and find data from there, every data is stored as lines. There are lines, and data is stored line by line, such as:
student name student surname student phone etc.
When i take an input of "student name" it starts to print without printing the name itself, prints what comes after it, same happens if i search for surname, only phone will be printed out.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
FILE *filePtr;
filePtr=fopen("std.txt","r");
char char_input[50];
char string[500];
printf("%s","Please give an input of the phone number\n");
scanf("%s",char_input);
while(!feof(filePtr)){
fscanf(filePtr,"%s",string);
if(strcmp(string, char_input)== 0){
fgets(string,500,filePtr);
puts(string);
}
}
fclose(filePtr);
}
Text file:
Andrew Brooks 865 965 55
Input:
Andrew
Output:
Brooks 865 965 55
Desired output:
Andrew Brooks 865 965 55
Instead of incorrectly using feof() and fscanf(filePtr,"%s", ... to incorrectly read a line. Use fgets() to read a line of the file and convert to a string.
Test the return value of fgets() to see if input occurred.
Use strstr() to look for a matching sub-string within string.
Example:
while (fgets(string, sizeof string, filePtr)) {
if (strstr(string, char_input)){
fputs(string, stdout);
}
}
The function feof will only tell you whether a previous input operation has already encountered end-of-file. It won't tell you whether you have now reached the end of file, so that the next input operation will fail. That function function is unable to predict whether the next input operation to fscanf or fgets will fail. Therefore, it should generally not be used as a loop condition. See this question for further information: Why is “while ( !feof (file) )” always wrong?
In your case, feof may return false and the subsequent function call to fscanf may return EOF due to encountering end-of-file. In that case, your posted code will ignore the return value of fscanf and behave as if fscanf had succeeded, and your posted code will attempt to process the non-existant input. This is likely to result in a bug.
Therefore, instead of using the function feof to determine whether the loop should be continued, you should check the return value of the input function.
You could rewrite your loop like this:
while ( fscanf(filePtr,"%s",string) == 1 ) {
if ( strcmp(string, char_input ) == 0 ) {
fgets( string, 500, filePtr );
puts( string );
}
}
This will solve the problem mentioned above of not checking the return value of fscanf. However, depending on the exact input, it may also be possible that the function fgets will fail due to encountering end-of-file. Therefore, it would be better if your program also checked the return value of the function fgets, instead of simply assuming that the function succeeded.
Another problem is that the line
puts(string);
will only print the contents of string, which is " Brooks 865 965 55". However, you also want to print "Andrew", which was read by the fscanf function call but has been meanwhile overwritten by the fgets function call. The simplest solution would be to print it before it gets overwritten. However, this will not work if the user searches for "Brooks" instead of "Andrew", because the word "Andrew" will already have been discarded in the previous loop iteration. This is because calling fscanf(filePtr,"%s",string) in a loop will not read one line of input per loop iteration, but will instead read a single word per loop iteration (which is not very meaningful).
Another consequence of reading in the input file word by word using fscanf(filePtr,"%s",string) is that your won't be able to find a match for the phone number "865 965 55". This is because your program will first read "865" from the input file and determine that this "word" is not identical to the search string. It will then read "965" and determine the same thing. It will do the same for "55".
The best solution would probably be to redesign your loop so that it always reads exactly one line of input per loop iteration, instead of only one word per loop iteration. After reading in one line of input, you can then parse the line by splitting it into "first name", "last name" and "phone number" using the function sscanf.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
FILE *filePtr;
char search_string[50];
char line[200];
//open input file
filePtr = fopen( "std.txt", "r" );
if ( filePtr == NULL )
{
fprintf( stderr, "unable to open input file!\n" );
exit( EXIT_FAILURE );
}
//prompt user for input
printf( "Please enter search string: " );
//Note that the following code now uses "fgets" instead
//of "fscanf", because fscanf will only read a single
//word, when using the "%s" format specifier. This means
//that it would be unable to read the phone number
//"865 965 55" as an input string, because that line
//consists of three "words".
//read exactly one line of input from user
if ( fgets( search_string, sizeof search_string, stdin ) == NULL )
{
fprintf( stderr, "input failure!\n" );
exit( EXIT_FAILURE );
}
//remove newline character from input line by
//replacing it with terminating null character
search_string[strcspn(search_string,"\n")] = '\0';
//read exactly one line of input from the input file
//per loop iteration
while ( fgets( line, sizeof line, filePtr ) != NULL )
{
char first_name[50];
char last_name[50];
char phone_number[50];
//attempt to parse input
if (
sscanf(
line,
"%49s %49s %49[^\n]",
first_name,
last_name,
phone_number
)
!= 3
)
{
fprintf(
stderr,
"WARNING: skipping line due to parse error!\n"
);
continue;
}
//parsing was successful, so we can now search the
//3 individual fields for the search string
if (
strcmp( search_string, first_name ) == 0
||
strcmp( search_string, last_name ) == 0
||
strcmp( search_string, phone_number ) == 0
)
{
//remove newline character from input line by
//replacing it with terminating null character
line[strcspn(line,"\n")] = '\0';
//print entire input line of file for user
printf( "%s\n", line );
}
}
//cleanup
fclose(filePtr);
}
This program has the following behavior:
Please enter search string: Andrew
Andrew Brooks 865 965 55
Please enter search string: Brooks
Andrew Brooks 865 965 55
Please enter search string: 865 965 55
Andrew Brooks 865 965 55
Note that the code above is not perfect, as it has the following issues:
When using fgets, if the input line is too long to fit in the buffer, then the program will not detect this, although it should probably print an error message and quit, in such a situation.
If any of the fields "first name", "last name" or "phone number" is larger than 49 characters, the code does prevent a buffer overflow (which would possibly cause your program to crash), but it still doesn't handle this situation properly, for example by checking for such a situation and by printing an appropriate error message.
However, for your purposes, the code should probably be sufficient.
A more robust program, which fixes these issues, would be the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
//This function will read exactly one line of input using
//fgets and verify that the line was not too long for the
//input buffer. Note that the buffer size must be two bytes
//longer than the actual string length, because there must
//be space for the newline character and the terminating
//null character. The newline character will be overwritten
//with another terminating null character.
//On success, it will return true. If not further input is
//available due to end-of-file, it will return false.
//Otherwise, the function will not return, but will
//terminate the program with an error message.
bool get_one_line_of_user_input( char *buffer, int buffer_size )
{
char *p;
if ( fgets( buffer, buffer_size, stdin ) == NULL )
{
if ( feof( stdin ) )
{
return false;
}
else
{
fprintf( stderr, "input error!\n" );
exit( EXIT_FAILURE );
}
}
p = strchr( buffer, '\n' );
if ( p == NULL )
{
//No newline character was found. This could mean
//that the line was too long to store in the input
//buffer, in which case, the program should quit
//with an error message. However, it could also mean
//that input has been redirected to come from a
//file, and that this file ends with a line without
//a line ending. In that case, the missing newline
//character can be ignored.
if ( !feof( stdin ) )
{
fprintf( stderr, "line too long for buffer!\n" );
exit( EXIT_FAILURE );
}
}
else
{
//remove newline character
*p = '\0';
}
return true;
}
int main()
{
FILE *filePtr;
char search_string[50];
char line[200];
//open input file
filePtr = fopen( "std.txt", "r" );
if ( filePtr == NULL )
{
fprintf( stderr, "unable to open input file!\n" );
exit( EXIT_FAILURE );
}
//prompt user for input
printf( "Please enter search string: " );
//read exactly one line of input from user
if ( !get_one_line_of_user_input( search_string, sizeof search_string ) )
{
fprintf( stderr, "input failure!\n" );
exit( EXIT_FAILURE );
}
//read exactly one line of input from the input file
//per loop iteration
while ( get_one_line_of_user_input( line, sizeof line ) )
{
char first_name[50];
char last_name[50];
char phone_number[50];
//attempt to parse input
if (
sscanf(
line,
"%49s %49s %49[^\n]",
first_name,
last_name,
phone_number
)
!= 3
)
{
fprintf(
stderr,
"WARNING: skipping line due to parse error!\n"
);
continue;
}
//verify that none of the fields was too long
if (
strlen( first_name ) == 49
||
strlen( last_name ) == 49
||
strlen( phone_number ) == 49
)
{
//At least one buffer is full, and we have no way
//to determine whether the limit was exceeded or whether
//we are merely at the limit, so we must assume that
//the limit was exceeded.
fprintf(
stderr,
"WARNING: skipping line due to field length "
"limit exceeded!\n"
);
continue;
}
//parsing was successful, so we can now search the
//3 individual fields for the search string
if (
strcmp( search_string, first_name ) == 0
||
strcmp( search_string, last_name ) == 0
||
strcmp( search_string, phone_number ) == 0
)
{
//print entire input line of file for user
printf( "%s\n", line );
}
}
//cleanup
fclose(filePtr);
}

How to not count 1.0 as an integer

So I need to read an integer from the stdin where the user may input 1.0, however since this is a double I wouldn't want to accept it. However when I try the method below the 1.0 is converted to 1 and is accepted. I would also like to accept 0001 as a possible integer input as 1.
first_sentence_to_switch = 0;
char buf[15]; // large enough
int number;
wrong_input = 0;
scanf("%14s", buf); // read everything we have in stdin
// printf("buffer: %s", buf);
if (sscanf(buf, "%d", &number) == 1)
{
first_sentence_to_switch = number;
}
else
{
wrong_input = 1;
}
You can use the %n format option to tell how much was matched by an sscanf call to make sure there is no extra cruft on the line:
if (sscanf(buf, "%d %n", &number, &end) == 1 && buf[end] == 0) {
.. ok
} else {
.. not an integer or something else in the input (besides whitespace) after the integer
Note the space between the %d and %n to skip any whitespace that might exist at the end of the buffer (such as a newline if the input was read by fgets or getline)
How to read a whole line of input
The line
scanf("%14s", buf);
will never read a whole line of input. It will only read a single word of input (which can also consist of digits). For example, if the user enters invalid input such as
"39 jdsuoew"
on a single line, then it will only read the word "39" as input, leaving the rest of the line on the input stream. This means that your program will accept the input as valid, although it should probably be rejected in this case.
Even if the user only entered "39", then it will only read this number, but will leave the newline character on the input stream, which can cause trouble.
If you want to ensure that it reads the entire line, I recommend that you use the function fgets instead, as that function will always read a whole line of input (including the newline character), assuming that the size of the provided memory buffer is large enough to store the entire line.
char line[100];
//attempt to read one line of input
if ( fgets( line, sizeof line, stdin ) == NULL )
{
fprintf( stderr, "Input error!\n" );
exit( EXIT_FAILURE );
}
//search for newline character, to verify that entire line was read in
if ( strchr( line, '\n' ) == NULL )
{
fprintf( stderr, "Line was too long for input buffer!\n" );
exit( EXIT_FAILURE );
}
Note that the function strchr requires that you #include <string.h>. If, as you state in the comments section, you are not allowed to use that header file, then you will probably have to assume that the memory buffer was large enough for the entire line, without verifying it (which you are also doing in your code). Although it is possible to verify this without using the function strchr, I don't recommend doing this. If the buffer is made large enough, then it is unlikely (but still possible) for the line to not fit into the buffer.
Convert string to integer using strtol
After reading the input line into a memory buffer, you can either use the function sscanf or strtol to attempt to convert the integer to a number. I recommend that you use the function strtol, because the function sscanf has undefined behavior if the user enters a number that is too large to be represented as a long int, whereas the function strtol is able to report such an error condition reliably.
In order to convert the line that you read to an integer, you could simply call strtol like this:
long l;
l = strtol( line, NULL, 10 );
However, calling the function with the second argument set to NULL has the same problem as calling the function atoi: You have no way of knowing whether the input was successfully converted, or if a conversion error occured. And you also have no way of knowing how much of the input was successfully converted, and whether the conversion failed prematurely, for example due to the user entering the decimal point of a floating-point number.
Therefore, it is better to call the function like this:
long l;
char *p;
l = strtol( line, &p, 10 );
Now, the pointer p will point to the first character that was not successfully converted to a number. In the ideal case, it will be pointing to the newline character at the end of the line (or maybe the terminating null character if you are not using fgets). So you could verify that the whole line was converted, and that at least one character was converted, like this:
if ( p == line || *p != '\n' )
{
printf( "Error converting number!\n" );
exit( EXIT_FAILURE );
}
However, this is maybe a bit too strict. For example, if the user enters "39 " (with a space after the number), the input will be rejected. You probably would want to accept the input in this case. Therefore, instead of requiring that p is pointing to the newline character and thereby not accepting any other remaining characters on the line, you may want permit whitespace characters to remain in the line, like this:
if ( p == line )
{
printf( "Error converting number!\n" );
exit( EXIT_FAILURE );
}
while ( *p != '\n' )
{
//verify that remaining character is whitespace character
if ( !isspace( (unsigned char)*p ) )
{
printf( "Error converting number!\n" );
exit( EXIT_FAILURE );
}
p++;
}
Note that you must #include <ctype.h> in order to use the function isspace.
Also, as previously stated, the advantage of using the function strtol over sscanf is that it can reliably report whether the number is too large or too small to be representable as a long int. If such an error condition occurs, it will set errno to ERANGE. Note that you must #include <errno.h> in order to use errno.
long l;
char *p;
errno = 0; //make sure that errno is not already set to ERANGE
l = strtol( line, &p, 10 );
if ( errno == ERANGE )
{
printf( "Number out of range!\n" );
exit( EXIT_FAILURE );
}
Code example of fgets and strtol
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
int main( void )
{
char line[100], *p;
long l;
//prompt user for input
printf( "Please enter an integer: " );
//attempt to read one line of input
if ( fgets( line, sizeof line, stdin ) == NULL )
{
fprintf( stderr, "Input error!\n" );
exit( EXIT_FAILURE );
}
//search for newline character, to verify that entire line was read in
if ( strchr( line, '\n' ) == NULL )
{
fprintf( stderr, "Line was too long for input buffer!\n" );
exit( EXIT_FAILURE );
}
//make sure that errno is not already set to ERANGE
errno = 0;
//attempt to convert input to integer
l = strtol( line, &p, 10 );
//verify that conversion was successful
if ( p == line )
{
printf( "Error converting number!\n" );
exit( EXIT_FAILURE );
}
//check for range error
if ( errno == ERANGE )
{
printf( "Number out of range!\n" );
exit( EXIT_FAILURE );
}
//verify that there are either no remaining characters, or that
//all remaining characters are whitespace characters
while ( *p != '\n' )
{
//verify that remaining character is whitespace character
if ( !isspace( (unsigned char)*p ) )
{
printf( "Error converting number!\n" );
exit( EXIT_FAILURE );
}
p++;
}
//print valid input
printf( "Input is valid.\nYou entered: %ld\n", l );
}
This program has the following output:
Valid input:
Please enter an integer: 39
Input is valid.
You entered: 39
Junk after valid input on same line:
Please enter an integer: 39 jdsuoew
Error converting number!
Attempt to enter floating-point number instead of integer:
Please enter an integer: 1.0
Error converting number!
Attempt to enter number that is so large that it is not representable as a long int:
Please enter an integer: 10000000000000000000000000
Number out of range!
Since there could be a bunch of possible wrong inputs, you should probably look only for right ones: '1' and '0'.
'I would also like to accept 0001 ...'
I only assume from your explanation that you wouldn't want to accept something
like: 0011
I would look from the end of buffer towards beginning.
In another words:
I'd look only for single '1' at the end of buffer and then only for '0' (zeros)
until you reach the beginning of buf.
Everything else is a wrong input.
Since you arbitrarely choose buffer size, you could write something like:
#define BUFF_SZ 15
...
char buf[BUFF_SZ];
...
while (buf[++i]); // <-- to avoid measuring buffer size at runtime.
This is an example of code with a function that returns correct result:
#include <stdio.h>
int check_input (char *buf);
int main()
{
char buf[15]; // large enough
scanf("%14s", buf);
if (check_input(buf) == 0) { printf("Wrong input!"); return(1); };
... input OK ...
return (0);
}
// function returns: 1: on success, 0: on wrong input
int check_input (char *buf)
{
int i=0;
while (buf[++i]); // it will stop counting when NULL char is found ..
// so it's like 'strlen(buff)'
// without unnecessary including <string.h>
// buffer is set at end so check if it ends with '1' ..
if (buf[--i] != '1') return (0);
// now, check all buffer backwards to make sure that ALL of those are '0's..
while ((--i) > 0)
if (buf[i] != '0') return (0);
return (1);
}
I've written most important part as a funtion so it would be more readable.
I hope that could help.

How to remove blank elements from an array of strings in C?

I was working on file inputs. I wanted to store each line as a string in array. For example: if the file has lines:
This is line 1.
This is line 2.
This is line 3.
The string should contain:
char str[][] = {"This is line 1.", "This is line 2.", "This is line 3."};
When I was trying out with extra spaces:
This is line 1.
This is line 2.
This is line 3.
The output was in the same format.
I want to delete those extra empty lines from my array of sentences, so that the output is same as before. How should I do that?
[EDIT] I am using following loop to enter sentences from file to the array:
while (fgets(str[i], LINE_SIZE, fp) != NULL)
{
str[i][strlen(str[i]) - 1] = '\0';
i++;
}
You should use an intermediate one-dimensional character array in the call of fgets like for example
for ( char line[LINE_SIZE]; fgets( line, LINE_SIZE, fp) != NULL; )
{
if ( line[0] != '\n' )
{
line[ strcspn( line, "\n" ) ] = '\0';
strcpy( str[i++], line );
}
}
If a line can contain blanks you can change the condition of the if statement the following way
for ( char line[LINE_SIZE]; fgets( line, LINE_SIZE, fp) != NULL; )
{
size_t n = strspn( line, " \t" );
if ( line[n] != '\n' && line[n] != '\0' )
{
line[ n + strcspn( line + n, "\n" ) ] = '\0';
strcpy( str[i++], line );
}
}
In the above code snippet you can substitute this statement
strcpy( str[i++], line );
for this statement if you want that the string would not contain leading spaces.
strcpy( str[i++], line + n );

Replacing a part of string with another string in C

this is my source code. When I input a string "I am in CSE 2nd year." and replace CSE 2nd(ie loc 9 to 15) by ECE 3rd, I get some garbage vales at the end of string. Also there is a newline at the beggining of rslt2 string. There is something wrong with rslt2. Can anyone please rectify the error?
//splitting a string and replace latter part of string by another string
#include<stdio.h>
#include<string.h>
int main()
{
int i,count=0,loc2,scount=0,rcount=0,loc=0; //scount represents counter for subset and rcount for replacement and loc from where we will split the string
char str[100],sub[100],newss[100],rslt[100],rslt2[100]; //newss=new substr, rslt and rslt2=former and latter part of original string
printf("Enter a String:\n");
fgets(str,100,stdin);
printf("\nString Entered by User:\n");
fflush(stdin);
puts(str);
printf("\nLoc Char\n"); //Creates Colums 'Char' and 'Loc'
for(i=0;str[i]!='\0';i++)
{
count++; //Counts length of String
printf("%d. %c\n",count,str[i]); //Prints Characters with it its Corresponding Location
}
printf("\n\nLength of String: %d\n\n",count);
printf("Enter the locations of Characters from where subset will start and end: \n");
scanf("%d%d",&loc,&loc2); //stores indices of begining and end of substring
printf("\n\nSubset formed from Existing String:\n");
for(i=loc-1;i<loc2;i++)
{
scount++;
sub[i]=str[i]; //stores substring in "sub"
printf("%c",sub[i]);
}
printf("\n\nLength of Subset: %d\n",scount);
for(i=0;i<(loc-1);i++)
{
rslt[i]=str[i]; //Stores former part of string in resultant string
}
for(i=loc2;i<strlen(str);i++)
{
rslt2[i]=str[i]; //Stores latter part of string in resultant string2
}
printf("\n\nEnter a Replacement for Subset(Of Equal Length as that of Subset):\n");
fflush(stdin);
fgets(newss,100,stdin);
for(i=0;newss[i]!='\0';i++)
rcount++;
printf("\n\nLength of New Subset: %d\n",rcount-1); //-1 to subtract length of null char
if(rcount-1!=scount) //to check whether replacement string and substring are of same len
printf("\nSince length of both subsets is not same. \nHence Replacement is Not Possible\n");
else //Concatination of 3 substrings
{
printf("\nResultant String:\n");
for(i=0;i<(loc-1);i++)
printf("%c",rslt[i]);
printf("\n");
for(i=0;newss[i]!='\0';i++)
printf("%c",newss[i]);
for(i=loc2;rslt2[i]!='\0';i++)
printf("%c",rslt2[i]);
}
return 0;
}
Here's an example of how to implement that program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXL 100
int main( void )
{
int count, lengthI, lengthR, start, end;
char initial[MAXL], replacment[MAXL], range[MAXL], result[MAXL];
// get user input
printf( "Initial string: " );
fflush( stdout );
fgets( initial, MAXL, stdin );
printf( "Replacement string: " );
fflush( stdout );
fgets( replacment, MAXL, stdin );
printf( "Start and end: ");
fflush( stdout );
fgets( range, MAXL, stdin );
count = sscanf( range, "%d%d", &start, &end ); // indices of beginning and end of range in initial string
// remove newline character from the input strings, if necessary
lengthI = strlen( initial );
if ( lengthI > 0 && initial[lengthI - 1] == '\n' )
initial[--lengthI] = '\0';
lengthR = strlen( replacment );
if ( lengthR > 0 && replacment[lengthR - 1] == '\n' )
replacment[--lengthR ] = '\0';
// range checking to verify that user inputs are valid and the resulting string will fit into the buffer
if ( count != 2 || start < 0 || start > lengthI || end < start || end > lengthI )
{
fprintf( stderr, "Invalid start and end values\n" );
exit( 1 );
}
if ( lengthI + lengthR - (end - start) + 1 > MAXL )
{
fprintf( stderr, "Resulting string would be too long\n" );
exit( 2 );
}
// create a new string with the substring replaced
if ( start > 0 ) // copy characters from the initial string up to the start index
strncpy( result, initial, start ); // note: this step may leave the result string unterminated
strcpy( &result[start], replacment ); // append the repacement string
// guarantees the result string is terminated
if ( end < lengthI ) // append characters from the initial that are after the end index
strcat( result, &initial[end] ); // terminates the result string (provided that strcat is called)
// print the result
printf( "%s\n", result );
}
Comments:
Don't mix fgets and scanf. Even if you're aware of the issues involved with doing so, it's still easy to get it wrong. Best to just read lines with fgets, and then parse with sscanf as necessary.
fflush(stdin) is non-standard. fflush is only guaranteed to work on stdout. On some systems, fpurge can be used to erase unread input.
When dealing with strings in C (aka arrays of characters), range checking is essential. Buffer overruns are the #1 cause of crashes, unexpected behavior, and security holes in C programs. Always range check user inputs, and always verify that a newly created string will fit into the buffer provided.
Always make sure that a newly created string ends with a null character (aka '\0'), and be sure to include that null character when calculating buffer sizes.
Note that in the sample code, strncpy may leave the string unterminated. The subsequent call to strcpy will terminate the string. The strcat function will also terminate the string. However, the call to strcat is conditional based on user input. Hence, without the call to 'strcpy', we'd have extra work to do to guarantee the the string gets its mandatory null terminator.
use negated scanf ie scanf(" %[^\n]",str_name); instead of gets();

C fgets question

struct DVDInfo *ReadStruct( void ) {
struct DVDInfo *infoPtr;
int num;
char line[ kMaxLineLength ];
char *result;
infoPtr = malloc( sizeof( struct DVDInfo ) );
if ( NULL == infoPtr ) {
printf( "Out of memory!!! Goodbye!\n" );
exit( 0 );
}
printf( "Enter DVD Title: " );
result = fgets( line, kMaxLineLength, stdin );
line[ strlen( line ) - 1 ] = '\0';
infoPtr->title = MallocAndCopy( line );
printf( "Enter DVD comment: " );
result = fgets( line, kMaxLineLength, stdin );
line[ strlen( line ) - 1 ] = '\0';
infoPtr->comment = MallocAndCopy( line );
do {
printf( "Enter DVD Rating (1-10): " );
scanf( "%d", &num );
Flush();
}
while ( ( num < 1 ) || ( num > 10 ) );
infoPtr->rating = num;
printf( "\n----------\n" );
return( infoPtr );
}
What is the purpose of even having the variable "result" above? Nothing is done with it. The pointer returned from fgets is stored into it, but that is it, it has no purpose.
You should test that result for NULL, to check for an EOF condition or an error, instead of just ignoring it. Also, by not checking result, you are doing an strlen on line, which could have uninitialized data, because fgets failed. Really, you should have, after the fgets:
if (!result)
{
free(infoPtr); // To not leak the object allocated at the start
return NULL; // Function failed
}
You might still have leaks, if the first fgets succeeds and the second fails, because there are additional allocation to pointer members of the structure. Unfortunately, because the struct was not initialized to zero, you can't check those pointers for NULL. So, perhaps using calloc instead of malloc or at least initializing all structure pointer members to NULL, would have been a better idea.
It seems as though someone started to implement error checking, but botched it in the end. The return value should be compared with NULL, with an error reported if equal.
Most likely, the compiler threw a warning about a function return value that was ignored. The programmer didn't care about the return value of fgets and simply added in the result = to make the compiler quit nagging about it. The correct solution would be to check the return value to make sure the function completed successfully.

Resources