C language reading from file and placing in variables - c

In a text file I have the form Type:Username Password, how do I place it in three different variables, so that the variable Type is in the variable type, username is in username, and password is in password in C ?
Example:
Admin:Username Password
How to make?
Type:Admin
User:Username
Pw:Password
Here's my code:
int ch;
int i = 0;
while ((ch = fgetc(fp)) != EOF) {
// Check for the colon character
if (ch == ':') {
// We have reached the end of the type string
// Move to the next variable
i = 0;
continue;
}
// Check for the space character
if (ch == ' ')
{
// We have reached the end of the username string
// Move to the next variable
i = 0;
continue;
}
// Store the character in the appropriate variable
if (i < 50) {
if (type[0] == 0) {
type[i] = ch;
} else if (username[0] == 0) {
username[i] = ch;
} else {
password[i] = ch;
}
i++;
}
}

Considering your initial requirement that you posted in your Question, that a text file consists of following line:
Admin:Username Password
To store Admin in variable type, Username in variable username and similarly Password in variable Password.
You can declare a structure something similar to:
typedef struct user {
char type[512];
char username[512];
char password[512];
} user;
And as #IngoLeonhardt commented, that strtok() or strchr() can be used to parse the line read from the text file, you can refer the below simple example code snippet to understand and further improve the logic.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct user {
char type[512];
char username[512];
char password[512];
} user;
int main(void)
{
FILE *file = fopen("test.txt","r");
char *line = NULL;
size_t linesize = 0;
char *token;
const char colon[2] = ":";
const char space[2] = " ";
user user_1;
if (file)
{
while (getline(&line, &linesize, file) != -1)
{
printf("%s\n", line);
token = strtok(line, colon);
strcpy(user_1.type, token);
token = strtok(NULL, space);
strcpy(user_1.username, token);
token = strtok(NULL, space);
strcpy(user_1.password, token);
printf("%s\n", user_1.type);
printf("%s\n", user_1.username);
printf("%s", user_1.password);
free(line);
}
fclose(file);
}
return 0;
}
PS: There may be some flaws/bugs in above implementation, please consider the above code just as an example.

Here is a solution which reads one character at a time from the file:
#include <stdio.h>
#include <stdlib.h>
#define MAX_STRING_SIZE 200
int main( void )
{
char type[MAX_STRING_SIZE];
char user[MAX_STRING_SIZE];
char pw[MAX_STRING_SIZE];
FILE *fp;
int ch;
int i;
//open the input file
fp = fopen( "input.txt", "r" );
if ( fp == NULL )
{
fprintf( stderr, "Error opening file!\n" );
exit( EXIT_FAILURE );
}
//read first token
i = 0;
while ( ( ch = fgetc( fp ) ) != ':' )
{
if ( ch == EOF )
{
fprintf( stderr, "Input error!\n" );
exit( EXIT_FAILURE );
}
if ( i == MAX_STRING_SIZE )
{
fprintf( stderr, "Sub-string is too long!\n" );
exit( EXIT_FAILURE );
}
type[i++] = ch;
}
type[i] = '\0';
//read second token
i = 0;
while ( ( ch = fgetc( fp ) ) != ' ' )
{
if ( ch == EOF )
{
fprintf( stderr, "Input error!\n" );
exit( EXIT_FAILURE );
}
if ( i == MAX_STRING_SIZE )
{
fprintf( stderr, "Sub-string is too long!\n" );
exit( EXIT_FAILURE );
}
user[i++] = ch;
}
user[i] = '\0';
//read third token
i = 0;
while ( ( ch = fgetc( fp ) ) != EOF )
{
if ( i == MAX_STRING_SIZE )
{
fprintf( stderr, "Sub-string is too long!\n" );
exit( EXIT_FAILURE );
}
pw[i++] = ch;
}
pw[i] = '\0';
//close the file
fclose( fp );
//print the three strings
printf( "Type:%s\n", type );
printf( "User:%s\n", user );
printf( "Pw:%s\n", pw );
}
For the input stated in the question, this program has the desired output:
Type:Admin
User:Username
Pw:Password
Here is an alternative solution, which reads a whole line at once:
This solution reads a line from the file as a string, by using the function fgets. After doing that, it uses the function strchr to find the ':' and ' ' delimiter characters in the string. Once they are found, the characters between the tokens can be copied so that they form a string.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define MAX_STRING_SIZE 200
bool read_exactly_one_line( char buffer[], int buffer_size, FILE *fp );
int main( void )
{
char type[MAX_STRING_SIZE];
char user[MAX_STRING_SIZE];
char pw[MAX_STRING_SIZE];
FILE *fp;
char line[500];
char *p, *q;
int token_length;
//open the input file
fp = fopen( "input.txt", "r" );
if ( fp == NULL )
{
fprintf( stderr, "Error opening file!\n" );
exit( EXIT_FAILURE );
}
//read one line of input
if ( ! read_exactly_one_line( line, sizeof line, fp ) )
{
fprintf( stderr, "File is empty!\n" );
exit( EXIT_FAILURE );
}
//find the delimiter between the first and second token
p = strchr( line, ':' );
if ( p == NULL )
{
fprintf( stderr, "Unable to find first delimiter!\n" );
exit( EXIT_FAILURE );
}
//verify that first token is not too large
token_length = p - line;
if ( token_length >= MAX_STRING_SIZE )
{
fprintf( stderr, "Token too large!\n" );
exit( EXIT_FAILURE );
}
//copy the first token
memcpy( type, line, token_length );
type[token_length] = '\0';
//move pointer to start of second token
p++;
//find the delimiter between the second and third token
q = strchr( p, ' ' );
if ( q == NULL )
{
fprintf( stderr, "Unable to find second delimiter!\n" );
exit( EXIT_FAILURE );
}
//verify that second token is not too large
token_length = q - p;
if ( token_length >= MAX_STRING_SIZE )
{
fprintf( stderr, "Token too large!\n" );
exit( EXIT_FAILURE );
}
//copy the second token
memcpy( user, p, token_length );
user[token_length] = '\0';
//move pointer to start of third token
q++;
//verify that third token is not too large
token_length = strlen( q );
if ( token_length >= MAX_STRING_SIZE )
{
fprintf( stderr, "Token too large!\n" );
exit( EXIT_FAILURE );
}
//copy the third token
memcpy( pw, q, token_length );
pw[token_length] = '\0';
//close the file
fclose( fp );
//print the three strings
printf( "Type:%s\n", type );
printf( "User:%s\n", user );
printf( "Pw:%s\n", pw );
}
//This function will read exactly one line and remove the newline
//character, if it exists. On success, it will return true. If this
//function is unable to read any further lines due to end-of-file,
//it returns false. If it fails for any other reason, it will not
//return, but will print an error message and call "exit" instead.
bool read_exactly_one_line( char buffer[], int buffer_size, FILE *fp )
{
char *p;
//attempt to read one line from the stream
if ( fgets( buffer, buffer_size, fp ) == NULL )
{
if ( ferror( fp ) )
{
fprintf( stderr, "Input error!\n" );
exit( EXIT_FAILURE );
}
return false;
}
//make sure that line was not too long for input buffer
p = strchr( buffer, '\n' );
if ( p == NULL )
{
//a missing newline character is ok if the next
//character is a newline character or if we have
//reached end-of-file (for example if the input is
//being piped from a file or if the user enters
//end-of-file in the terminal itself)
if ( getc(fp) != '\n' && !feof(stdin) )
{
printf( "Line input was too long!\n" );
exit( EXIT_FAILURE );
}
}
else
{
//remove newline character by overwriting it with a null
//character
*p = '\0';
}
return true;
}
This program has the same output as the first one.

Related

How to detect a repeated " user input String " in a file?

The idea is like compare line by line of strings and detecting the duplicated ones to evade putting theme in another file
after I fill my file with names and created a new file to put all strings without the duplicated ones, I used this loop, but I don't know if it's right or nah. It didn't work
FILE *Tr , *temp;
char test[50] , test1[50];
Tr = fopen("test.txt","w");
temp = fopen("temp1.txt" , "r");
while( !feof(temp) )
{
fgets(test , 50 , temp);
while( !feof(temp) ){
if ( fgets(test , 50 , temp) == fgets(test1 , 50 , temp) ){
printf("a string exist in the file");
}
else{ fprintf(Tr, "%s" , test1);
}
}
}
The following line is wrong:
if ( fgets(test , 50 , temp) == fgets(test1 , 50 , temp) ){
Using == on pointers will compare the actual pointer values, i.e. the memory addresses. If you want to compare the actual string contents (i.e. what the pointers are pointing to), then you must use strcmp instead.
Also, you should only read from the input file, not the output file.
You should also remember all strings that you have read. Otherwise, you will have no way of determining whether the current line is a duplicate or not.
Additionally, it does not make sense having both an outer loop and an inner loop with the same loop condition:
while( !feof(temp) )
Also, using !feof(temp) as a loop condition is generally wrong. See this question for further information:
Why is “while ( !feof (file) )” always wrong?
The following program will remember up to 100 strings, each up to 100 chars in length (including the terminating null character).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define MAX_STRINGS 100
#define MAX_STRING_LEN 100
int main( void )
{
FILE *input, *output;
char strings[MAX_STRINGS][MAX_STRING_LEN];
int num_strings = 0;
char line[MAX_STRING_LEN];
//open input file
input = fopen( "input.txt", "r" );
if ( input == NULL )
{
fprintf( stderr, "Error opening input file!\n" );
exit( EXIT_FAILURE );
}
//open output file
output = fopen( "output.txt", "w" );
if ( output == NULL )
{
fprintf( stderr, "Error opening output file!\n" );
exit( EXIT_FAILURE );
}
//read one line of input per loop iteration
while ( fgets( line, sizeof line, input ) != NULL )
{
bool is_duplicate = false;
char *p;
//find newline character
p = strchr( line, '\n' );
//make sure that input buffer was large enough to
//read entire line, and remove newline character
//if it exists
if ( p == NULL )
{
if ( !feof( input ) )
{
fprintf( stderr, "Line was too long for input buffer!\n" );
exit( EXIT_FAILURE );
}
}
else
{
//remove newline character
*p = '\0';
}
//determine whether line is duplicate
for ( int i = 0; i < num_strings; i++ )
{
if ( strcmp( line, strings[i] ) == 0 )
{
is_duplicate = true;
break;
}
}
if ( !is_duplicate )
{
//remember string
strcpy( strings[num_strings++], line );
//write string to output file
fprintf( output, "%s\n", line );
}
}
//cleanup
fclose( output );
fclose( input );
}
Given the input
String1
String2
String3
String4
String5
String1
String6
String2
String1
String7
String8
String1
String2
this program has the following output:
String1
String2
String3
String4
String5
String6
String7
String8
As you can see, all duplicate strings were properly filtered out of the output.
However, using a statically sized array is a bit of a waste of space, and it also imposes a hard limit. Therefore, it may be better to use dynamic memory allocation instead:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define INITIAL_CAPACITY 100
#define MAX_LINE_LEN 200
int main( void )
{
FILE *input, *output;
char **strings;
size_t strings_capacity;
int num_strings = 0;
char line[MAX_LINE_LEN];
//open input file
input = fopen( "input.txt", "r" );
if ( input == NULL )
{
fprintf( stderr, "Error opening input file!\n" );
exit( EXIT_FAILURE );
}
//open output file
output = fopen( "output.txt", "w" );
if ( output == NULL )
{
fprintf( stderr, "Error opening output file!\n" );
exit( EXIT_FAILURE );
}
//set capacity of "strings" array to INITIAL_CAPACITY
strings_capacity = INITIAL_CAPACITY;
strings = malloc( strings_capacity * sizeof *strings );
if ( strings == NULL )
{
fprintf( stderr, "Memory allocation failure!\n" );
exit( EXIT_FAILURE );
}
//read one line of input per loop iteration
while ( fgets( line, sizeof line, input ) != NULL )
{
bool is_duplicate = false;
char *p;
//find newline character
p = strchr( line, '\n' );
//make sure that input buffer was large enough to
//read entire line, and remove newline character
//if it exists
if ( p == NULL )
{
if ( !feof( input ) )
{
fprintf( stderr, "Line was too long for input buffer!\n" );
exit( EXIT_FAILURE );
}
}
else
{
//remove newline character
*p = '\0';
}
//determine whether line is duplicate
for ( int i = 0; i < num_strings; i++ )
{
if ( strcmp( line, strings[i] ) == 0 )
{
is_duplicate = true;
break;
}
}
if ( !is_duplicate )
{
//expand capacity of "strings" array if necessary
if ( num_strings == strings_capacity )
{
strings_capacity *= 2;
strings = realloc( strings, strings_capacity * sizeof *strings );
if ( strings == NULL )
{
fprintf( stderr, "Memory allocation failure!\n" );
exit( EXIT_FAILURE );
}
}
//remember string
strings[num_strings] = malloc( strlen( line ) + 1 );
if ( strings[num_strings] == NULL )
{
fprintf( stderr, "Memory allocation failure!\n" );
exit( EXIT_FAILURE );
}
strcpy( strings[num_strings], line );
num_strings++;
//write string to output file
fprintf( output, "%s\n", line );
}
}
//cleanup
//free all dynamically allocated memory
for ( int i = 0; i < num_strings; i++ )
free( strings[i] );
free( strings );
//close file handles
fclose( output );
fclose( input );
}

Segmentation fault at the last line when reading file

I got a segmentation fault when reading a file. There is an empty line at the end of the file. I thought when I did fscanf( input, "[^\n]\n", c ), it would take the next line tab from the second last line and the token went to the empty line at the end, which will be '\0', but I got the a segmentation fault.
FILE *input = fopen( filename, "r" );
if ( input == NULL ) {
fprintf( stderr, "Can't open file: %s\n", filename );
exit( EXIT_FAILURE );
}
file = ( char ** )malloc( MAX_LINE * sizeof( char * ) );
int index = 0;
char *c = ( char * )malloc( MAX_CHAR * sizeof( char ) );
int length = 0;
while ( fscanf( input, "%[^\n]\n", c ) != EOF ) {
if ( c == NULL ) {
fprintf( stderr, "Cannot read line: %d ", index );
exit( EXIT_FAILURE );
}
length = ( int ) strlen( c );
// checking if a line has too many characters
if ( length > MAX_CHAR ) {
fprintf( stderr, "Line too long: %s:%d\n", filename, ( index + 1 ) );
exit( EXIT_FAILURE );
}
//checking if the file has too many lines
if ( index > MAX_LINE ) {
fprintf( stderr, "Too many lines: %s\n", filename );
exit( EXIT_FAILURE );
}
//printf( "%s\n%d %d\n", c, index, length );
file[ index ] = ( char * )malloc( MAX_CHAR * sizeof( char ) );
file[ index ] = c;
index++;
}

Read character by character from a file and put each line in a String

I've to read character by character from a file and put each line in a String.
The problem is that i don't know the size of each line so eventually I've to reallocate the memory. So If I try a reallocation my program return error. Am I doing something wrong?
FILE * file = fopen(input,"r");
if(file != NULL){
char temp;
char * line;
line = (char *) malloc(sizeof(char) * 10);
int i = 0;
while((temp = fgetc(file)) != EOF){
if(temp == '\n'){
i = 0;
}
else{
if(i > strlen(line) - 2){
line = (char *) realloc(line,sizeof(line) * 10);
}
line[i] = (char) temp;
i++;
}
}
free(line);
fclose(file);
}
else{
}
the following proposed code:
cleanly compiles
performs the desired functionality
properly checks for errors
outputs user error messages to stderr
outputs the text reason the system thinks an error occurred to stderr
documents why each header file is included
shows an example of how to handle the case where the user failed to enter a command line parameter (in this case a input file name)
makes use of size_t rather than int when passing parameters to malloc() and realloc()
and now, the proposed code:
#include <stdio.h> // fopen(), perror(), fclose() fprintf()
#include <stdlib.h> // exit(), EXIT_FAILURE, malloc(), realloc(). free()
int main( int argc, char *argv[] )
{
if( argc != 2 )
{
fprintf( stderr, "USAGE: %s <fileName>\n", argv[0] );
exit( EXIT_FAILURE );
}
FILE * file = fopen( argv[1], "r" );
if( !file )
{
perror( "fopen failed" );
exit( EXIT_FAILURE );
}
// implied else, fopen successful
int ch;
char * line = malloc( 10 );
if( !line )
{
perror( "malloc failed" );
fclose( file ); // cleanup
exit( EXIT_FAILURE );
}
// implied else, malloc successful
size_t lineLen = 10;
size_t i = 0;
while( (ch = fgetc(file)) != EOF )
{
if( ch == '\n' )
{
line[i] = '\0';
// do something with contents of line
i = 0;
}
else
{
if(i >= lineLen )
{
lineLen *= 2;
char * temp = realloc( line, lineLen );
if( !temp )
{
perror( "realloc failed" );
// cleanup
fclose( file );
free( line );
exit( EXIT_FAILURE );
}
line = temp;
}
line[i] = (char)ch;
i++;
}
}
free(line);
fclose(file);
}

Why is my program suddenly terminating before executing scanf()?

I am trying to write code to replace a line from a text file. It compiles successfully, but it terminates out of nowhere as soon as it tries to scan the line number to be replaced.
I really have no idea what I am doing wrong. I have also tried with fgets() but it still doesn't work.
#include <stdio.h>
#include <stdlib.h>
#define MAXNAME 30
#define MAXLINE 256
int main(){
char fileName[MAXNAME];
FILE *originalFileCheck;
printf("Input the name of the file to be opened: ");
scanf("%s", fileName);
originalFileCheck = fopen(fileName, "r");
if(originalFileCheck == NULL){
printf("The file %s was not opened successfully. The program will now terminate.\n", fileName);
exit(1);
}
else{
FILE *tempFileWrite;
char tempName[MAXNAME] = "temp.txt";
tempFileWrite = fopen(tempName, "w");
char newLine[MAXLINE];
int lineNum;
printf("Input the content of the new line: ");
scanf("%s", newLine);
printf("Input the number of the line you want to replace: ");
scanf("%d", &lineNum); /* it terminates WITHOUT scanning this int*/
char str[MAXLINE];
int counter = 1;
while(fgets(str, MAXLINE, originalFileCheck) != NULL){
if(counter != lineNum){
for(int i = 0; str[i] != '\0' && str[i] != '\n'; i++){
fputc(str[i], tempFileWrite);
}
fprintf(tempFileWrite, "\n");
}
else{
fprintf(newLine, "%s\n", tempFileWrite);
}
counter++;
}
fclose(tempFileWrite);
fclose(originalFileCheck);
...
return 0;
}
the following proposed code:
cleanly compiles
checks for input errors
checks for output errors
performs the desired functionality
properly cleans up when an error occurs
and now, the proposed code:
#include <stdio.h>
#include <stdlib.h>
#define MAXNAME 30
#define MAXLINE 256
int main( void )
{
char fileName[MAXNAME];
FILE *originalFileCheck;
printf("Input the name of the file to be opened: ");
if( !fgets( filename, sizeof( fileName ), stdin) )
{
fprintf( stderr, "fgets to input 'original' file name failed\n" );
exit( EXIT_FAILURE );
}
// remove trailing newline
fileName[ strcspn( fileName, "\n" ) ] = '\0';
originalFileCheck = fopen( fileName, "r" );
if( !originalFileCheck )
{
perror( "fopen original file for read failed" );
exit( EXIT_FAILURE );
}
FILE *tempFileWrite;
char tempName[ MAXNAME ] = "temp.txt";
tempFileWrite = fopen( tempName, "w" );
if( !tempFileWrite )
{
perror( "fopen to write new file failed" );
fclose( originalFileCheck );
exit( EXIT_FAILURE );
}
char newLine[ MAXLINE ];
int lineNum;
printf("Input the content of the new line: ");
if( !fgets( newLine, sizeof( newLine ), stdin ) )
{
perror"fgets to input new line content failed" );
fclose( originalFileCheck );
exit( EXIT_FAILURE );
}
printf("Input the number of the line you want to replace: ");
if( scanf("%d", &lineNum) != 1 )
{
fprintf( stderr, "scanf for replacement line number failed\n" );
fclose( originalFileCheck );
fclose( tempFileWrite );
exit( EXIT_FAILURE );
}
char str[MAXLINE];
int counter = 1;
while( fgets(str, sizeof( str ), originalFileCheck) )
{
if(counter != lineNum)
{
if( fputs( str, tempFileWrite ) == EOF )
{
perror( "fputs for original line failed" );
fclose( originalFileCheck );
fclose( tempFileWrite );
exit( EXIT_FAILURE );
}
}
else
{
if( fputs( newLine, tempFileWrite ) == EOF )
{
perror( "fputs for replacement line failed" );
fclose( originalFileCheck );
fclose( tempFileWrite );
exit( EXIT_FAILURE );
}
}
counter++;
}
fclose(tempFileWrite);
fclose(originalFileCheck);
return 0;
}

How to copy the returned token by strtok in c

When I try to copy the strtok through strcpy as suggested by the other answers on this forum. I don't get the respective number. File format is something like this 43,789,127.0.0.1 on each particular line. However I get 127 in the temp[2] location which should be 127.0.0.1 . What am I missing over here?
FILE *f = fopen("somefile", "r");
char *temp[3];
temp[0] = malloc(20);
temp[1] = malloc(20);
temp[2] = malloc(20);
const char s[1] = ",";
char *pch;
if(f != NULL)
{
char line[1024];
while(fgets(line, sizeof line, f) != NULL)
{
pch = strtok(line, s);
for(int i = 0; i<3; i++)
{
strcpy(temp[i],pch);
pch = strtok (NULL, s);
}
}
fclose(f);
} else
{
perror("somefile");
}
for(int i = 0; i<3; i++)
{
printf ("%s\n",temp[i]);
}
s is not a proper C string: const char s[1] = ","; defines it to have a size of 1, without a null terminator.
Use this instead:
const char *s = ",";
Note that a line with fewer than 2 commas will cause the program to have undefined behavior as you do not check that strtok() returns a non NULL pointer. Here is an alternative using sscanf():
#include <stdio.h>
int main(void) {
FILE *fp = fopen("somefile", "r");
char temp[3][20];
if (fp != NULL) {
char line[1024];
while (fgets(line, sizeof line, fp) != NULL) {
if (sscanf(line, "%19[^,],%19[^,],%19[^\n]", temp[0], temp[1], temp[2]) == 3) {
printf("%s\n%s\n%s\n\n", temp[0], temp[1], temp[2]);
}
}
fclose(fp);
}
return 0;
}
Note however that the above code will fail to parse lines with empty fields such as ,, because sscanf() requires a non empty string for the %[^,] conversion specifier.
Note also that strtok() would be inappropriate for such parsing too as it treats sequences of separators as a single separator, which is OK for white space but probably incorrect for ,.
the following proposed code:
cleanly compiles
is consistently indented
is appropriately horizontally and vertically spaced
corrects all the known problems in the posted code
properly checks for and handles errors
properly displays the parameters from all the lines in the file
eliminates unneeded variables
and now the code
#include <stdio.h> // fopen(), fclose(), fgets(), perror()
#include <stdlib.h> // exit(), EXIT_FAILURE
#include <string.h> // strtok(), strcpy()
#define MAX_LENGTH 20
#define MAX_PARAMETERS 3
int main( void )
{
FILE *f = fopen( "somefile", "r" );
if( !f )
{
perror( "fopen to read somefile failed" );
exit( EXIT_FAILURE );
}
// implied else, fopen successful
char *temp[ MAX_PARAMETERS ];
if( NULL == ( temp[0] = malloc( MAX_LENGTH ) ) )
{
perror( "malloc failed" );
exit( EXIT_FAILURE );
}
if( NULL == ( temp[1] = malloc( MAX_LENGTH ) ) )
{
perror( "malloc failed" );
exit( EXIT_FAILURE );
}
if( NULL == ( temp[2] = malloc( MAX_LENGTH ) ) )
{
perror( "malloc failed" );
exit( EXIT_FAILURE );
}
char line[1024];
while( fgets(line, sizeof line, f) )
{
int i;
char *pch = strtok( line, "," );
for( i = 0; i<3 && pch; i++ )
{
strcpy( temp[i], pch );
pch = strtok ( NULL, "," );
}
if( MAX_PARAMETERS != i )
{
printf( "failed to extract all parameters from line: %s\n", line );
}
else
{
for( int j = 0; j<MAX_PARAMETERS; j++ )
{
printf( "%s\n", temp[j] );
}
}
}
fclose( f );
} // end function: main

Resources