Information not remembered when dealing with multiple lines in a file - c

So let's say that a file has multiply lines each containing one word. I want to store the characters of every word in every line in a array. The code below clearly doesn't work because the -i is zeroed in every loop and the program starts storing characters in the 1st position of the array again. The code is:
while(1)
{
if(fgets(str, 50, fp) == NULL)
break;
for(i=0; i<strlen(str); i++)
p[i] = str[i];
}

you have separate counter variable for p and keep incrementing it to avoid
overwritting, like below.
int write_position = 0;
while(1)
{
if(fgets(str, 50, fp) == NULL)
break;
for(i=0; i<strlen(str); i++)
p[write_position++] = str[i]; // you will not lose previous ones here
}
at the end lenght of the array p is equal to write_position

Fix your file reading loop like this;
while (fgets(str, sizeof(yourString), fp) != NULL)
{
yourString[strlen(yourString) - 1] = '\0'; // delete the new line
printf("%s\n", yourString);
}
So simply in the above code, your while loop will be working until there is no another line to read in the file. In each turn of while loop, it will take one line from your file, and add it to your yourString char array. Notice that, fgets() will be taking newline characters too (\n) for every line in the file, so that we need to remove this characters from the array before we add another line in it.
Before the while loop, You need to declare a char array to store each line in it such as;
char yourString[stringSize];
You need to determine a stringSize for your array to make it has enough storage for your file.

the following proposed code snippet
performs the desired functionality
assumes that the array 'p[]' is an array of char pointers to pointers and contains (at least) enough entries to hold all the lines in the input file
no line in the input file is longer than 48 characters + newline
And now the proposed code:
#define MAX_LINE_LEN 50
char **p;
p = calloc( NUM_LINES_IN_FILE, sizeof( char * ) );
if( !p )
{
perror( "calloc failed" );
exit( EXIT_FAILURE );
}
// implied else, calloc successful
FILE *fp;
if( !(fp = fopen ( "inputFileName", "r" )) )
{
perror( "fopen failed" );
exit( EXIT_FAILURE );
}
// implied else, fopen successful
char str[ MAX_LINE_LEN ];
for( int i=0; fgets(str, sizeof( str ), fp); i++ )
{
// remove trailing newline char
str[ strcspn( str, '\n' ) ] = '\0';
p[i] = strdup( str );
}
fclose( fp );

Related

How to make the code to accept the next word not just the first one?

In this code, the problem which I am facing is, it only accepts the first word which has a comma at the end. The file has many words with commas at the end but it is accepting the first one. For example, if I gave the option to enter your ID card number which is not the first word. It could be the 2nd or 3rd word in the file then how I would handle it?
1st part
#define STRING_SIZE 49
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void RemoveNewLines( char * buffer ){
char * ptr;
ptr = strchr( buffer, '\n' );
if( ptr )
* ptr = 0;
ptr = strchr( buffer, '\r' ); // in case you see carriage returns also
if( ptr )
* ptr = 0;
}
2nd part
int main(){
char instr[STRING_SIZE+1], string[STRING_SIZE+1];
FILE * fr = NULL;
int flag = 0;
size_t length = 0;
fr = fopen("file.csv","r");
if( fr == NULL ){
printf( "Unable to open file\n" );
return 1;
}
printf("Enter your name: ");
fgets( instr, STRING_SIZE, stdin);
RemoveNewLines( instr );
strcat( instr, "," ); // append a comma to the user's entry
length = strlen( instr );
while( fgets( string, STRING_SIZE, fr ) ){
RemoveNewLines( string );
if( strncmp( instr, string, length ) == 0 ){
flag = 1;
break; } }
fclose(fr);
if( flag == 1 ) // I prefer positive logic{
printf( "Your details :" );
printf( "'%s'\n", string );
return 0;
}
printf("Access denied.\n");
return -1;
}
Well, you are comparing the beginning of a line in the file with whatever that was provided plus a , appended. So it is really what you asked it to do.
If you want to search on arbitrary fields, rather than this approach, I would split the line read from the csv, and compare the nth field with what was provided. Assuming this csv file is a basic one (no quotes or embedded commas/new-lines), you can easily do that by performing N strtok() operations on string.

Login system using text file in C

So my assignment is to create a login system. Username and passwords will be checked with content in the “account.txt” file. The content is that file have structure look like this:
Account ID: 1
Name: John Lee
Pass: 7uf
Role: student
Account ID: 2
Name: Park Lee
Pass: 42h
Role: Lecturer
Here what i got so far:
struct Account {
char name[20];
char pass[20];
};
void Login (char name[], char pass[]){
FILE *sc;
struct Account acc;
sc = fopen("Account.txt","r");
fscanf(sc,"\nName: %s",acc.name);
fscanf(sc,"\nPass: %s",acc.pass);
if(strcmp(name,acc.name) == 0 && strcmp(pass,acc.pass)) {
printf("Login successful");
}
else {
printf("Name or Pass incorrect");
}
fclose(sc);
}
int main () {
struct Account log[20];
fflush(stdin);
printf("\n\t\tEnter your name: ");
gets(log[20].name);
printf("\t\tEnter your password: ");
gets(log[20].pass);
Login(log[20].name,log[20].pass);
}
return 0; }
What do you guys think i should do ?
in the function: login() the code needs to check every entry in the file before declaring a failure. After all, the first entry might not be for the person trying to login
regarding:
sc = fopen("Account.txt","r");
fscanf(sc,"\nName: %s",acc.name);
1) always check (!=NULL) the returned value from fopen() to assure the operation was successful.
2) need to move past the first line of each entry in the input file before trying to read the name
3) when using the input format specifiers '%s' and/or '%[...]' always include a MAX CHARACTERS modifier that is 1 less than the length of the input buffer because those specifiers always append a NUL byte to the input. This avoids a buffer overflow and the resulting undefined behavior.
I.E.
if( !sc )
{
perror( "fopen failed" );
exit( EXIT_FAILURE );
}
{input first line of acct and discard}
if( fscanf(sc,"\nName: %19s",acc.name) != 1 )
{
// handle error
}
However, if those lines in the input file contains those labels, like Name: Then the code needs to also input and discard those labels, as in the above example.
This seems to be homework, so I'm very reluctant to just 'give' you appropriate code. I would expect your instructor or TA would be able to help you with the details of what the code should be doing.
regarding statements like:
gets(log[20].name);
1) gets() is no longer part of the C language, Your compiler should have told you this.
2) the valid index into an array has the range: 0...(number of entries in array -1). So index 20 is beyond the end of the range. Suggest just using a pointer to the array.
3) Suggest using `fgets() to input each line from the file.
4) the struct you have declared will not work well with the actual data from the input file.
Suggest using:
#define MAX_LOG_ENTRIES 20
int main( void )
{
struct Account acc[ MAX_LOG_ENTRIES ] = { "","" };
char dummy[128];
size_t i;
for( i = 0; i<MAX_LOG_ENTRIES; i++ )
{
if( i< MAX_LOG_ENTRIES && fgets( dummy, sizeof( dummy ), sc ) )
{ // then successfully read 'account' line
if( fgets( dummy, sizeof( dummy ), sc ) )
{ // then successfully read 'Name:` line
// remove trailing newline
dummy[ strcspn( dummy, "\n" )] = '\0';
// skip past Name: ' label
char * namePtr = strchr( dummy, ':' );
if( namePtr )
{ // then found the ':'
// step by ': '
namePtr += 2;
}
// extract name
strcpy( log[i].name, namePtr );
if( fgets( dummy, sizeof( dummy ), sc ) )
{ // then successfully read 'Pswd:` line
// remove trailing newline
dummy[ strcspn( dummy, "\n" )] = '\0';
// skip past Pswd: ' label
char * pswdPtr = strchr( dummy, ':' );
if( pswdPtr )
{ // then found the ':'
// step by ': '
pswdPtr += 2;
}
// extract password
strcpy( log[i].pswd, pswdPtr );
// read/discard unused data line
fgets( dummy, sizeof( dummy ), sc );
// read/discard unused blank line
fgets( dummy, sizeof( dummy ), sc );
}
When the above for() loop exits, all the records are read into the array named log[] and the variable 'i' contains the number of entries in the array 'log[]' that are actually used
now the code needs to input the two fields from the user (name and pswd)
Then loop through the array log[] to see if there is a 'name+pswd' match.
if fgets( dummy, sizeof( dummy ), sc );a match is found, then success, otherwise the user failed to enter valid data.
Note: The above code fails to check for errors and similar problems, including if the input file contains less than 20 entries. You should be able to add the error (and EOF) checking

Read lines from a file and create alphabetically sorted array

I am learning C and I want to do this specific task. I know there is a number of similar questions and answers, but still... I will try to be more specific. Lets say, I have a file with following lines:
program01
programs
aprogram
1program
prog
5program
And I want now an array with:
1program
5program
aprogram
prog
program01
programs
So there are ONLY latin small letters and numbers in strings, no spaces. I know how to perform some separate steps, but want to get and feel the whole (and proper) concept, so to say. Probably it could make some sorting decisions on the fly when reading from file first? Manual sort is preferred for my particular case, just for the sake of better learning and possible optimisation. Lets say, maximal length of one line is 256, maximal number of lines is 256. Thanks in advance.
Check the below code:
#include <stdio.h>
#include<string.h>
int main(void) {
char a[256][256];
int i=0,j=0,k=0,n;
while(i<256 && fgets(a[i],256,stdin) != NULL)
{
n = strlen(a[i]);
if(n >0 && a[i][n-1] == '\n')
a[i][n -1] = '\0';
i++;
}
for(j=0;j<i;j++)
{
char max[256];
strcpy(max,a[j]);
for(k=j+1;k<i;k++)
{
if(strcmp(a[k],max) < 0)
{
char tmp[256];
strcpy(tmp,a[k]);
strcpy(a[k],max);
strcpy(max,tmp);
}
}
strcpy(a[j],max);
}
for(j=0;j<i;j++)
{
printf("%s\n",a[j]);
}
return 0;
}
The following cleanly compiles
however, I have not tested it
you might want to modify it to get the file name from
the command line
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_ROWS (256)
#define MAX_COLUMNS (256)
#define FILE_NAME "myInputFile"
// prototypes
void bubbleSortWordsArray( int wordCount );
void printWordsArray( int wordCount );
static char words[MAX_ROWS][MAX_COLUMNS] = {{'\0','\0'}};
int main(void)
{
FILE *fp = NULL;
if( NULL == (fp = fopen( FILE_NAME, "r") ) )
{
perror( "fopen failed" );
exit( EXIT_FAILURE );
}
// implied else, fopen successful
// read each line from file into entry in words array
int i = 0;
while( fgets(words[i], MAX_COLUMNS, fp ) )
{
// remove trailing newline from string
words[i][strlen(words[i])-1] = '\0';
i++;
}
// 'i' contains number of valid entries in words[][]
// sort the array of strings
bubbleSortWordsArray(i);
printWordsArray(i);
return(0);
} // end function: main
void bubbleSortWordsArray( int wordCount )
{
int c; // outer index through rows
int d; // inner index through rows
char swap[MAX_COLUMNS] = {'\0'};
for (c = 0 ; c < ( wordCount - 1 ); c++)
{
for (d = 0 ; d < (wordCount - c - 1); d++)
{
if( 0 > strcmp( words[d], words[d+1] ) )
{ // then words need to be swapped
strcpy( swap, words[d] );
strcpy( words[d], words[d+1]);
strcpy( words[d+1], swap );
} // end if compare/swap
} // end for
} // end for each row
} // end function: bubbleSortWordsArray
void printWordsArray( int wordCount )
{
int i; // loop index
printf( "\n" ); // start on new output line
for( i=0; i<wordCount; i++ )
{
printf( "%s\n", words[i] );
}
} // end function: printWordsArray
Make a 2D char array
try to read it by using fscanf function(sorry i cant remember the syntax).
fscan will read your whole line till '\n' but there should not be space
and store each string in a row.
then sort it by comparaing the first index of each string

Inserting a word(char[]) into a structure word(char[]) in C

So i'm reading each word from a textfile which contains many words that are not unique. I'm supposed find the number of unique words and store those words into a structure variable called word
My main problem is labeled under the comment "MY PROBLEM IS HERE BELOW"
Struct Code:
typedef struct {
char word[101];
int freq;
} WordArray;
Code:
//temp[i].word is a temporary structure that scans ALL words
//input[i].word is the structure with unique words
word = fscanf(finput, "%s", &temp[i].word);
//if the word in temp[i] is in input[j].word then it is not unique
// so frequency of word is incremented
for(j = 0; j < 200; j++) {
if(strcmp(temp[i].word,input[j].word) == 0){
contains = 1;
input[j].freq++;
}
}
// MY PROBLEM IS HERE BELOW
if(contains != 1) { // since contains is not 1 then it is unique
input[i].word = temp[i].word // i want to put this word in input[i].word but this
// produces incompatible type error
// i tried strcpy and strncpy no luck...
input[i].freq++;
uniqueWords++;
}
Remove the &
fscanf(finput, "%s", &temp[i].word);
change it to
fscanf(finput, "%s", temp[i].word);
Or take the address of the first element, which is equivalent to the previous line
fscanf(finput, "%s", &temp[i].word[0]);
the passed array decays to a pointer to the first element of the array, so these both are equivalent
fscanf(finput, "%s", temp[i].word); /* passed a pointer to the first elemn */
fscanf(finput, "%s", &temp[i].word[0]); /* passed the address of the first element */
also, add a limit to fscanf to prevent buffer overflow like this
fscanf(finput, "%100s", temp[i].word); /* passed a pointer to the first elemn */
where the number should be 1 fewer than the array size.
You can't assign to arrays this line
input[i].word = temp[i].word
is wrong, you should use strcpy
strcpy(input[i].word, temp[i].word);
This line:
word = fscanf(finput, "%s", &temp[i].word);
Should really be more like:
if( 1 != fscanf(finput, " %100s", temp[i].word) )
{ // fscanf failed
perror( "fscanf failed");
exit( EXIT_FAILURE );
}
// implied else, fscanf successful
typedef struct {
char word[101];
int freq;
} WordArray;
That just obscures the code, making it more difficult to read/understand/maintain, much better to use:
#define MAX_WORDS (200)
#define MAX_WORD_LEN (100)
struct WordArray
{
char word[MAX_WORD_LEN+1];
int freq;
};
struct WordArray input[MAX_WORDS];
For reading words, counting duplicates:
int numWords = 0;
BOOL dupFound = false;
char tempWord[101];
// prep save area 'input[]'
memset( input, 0x00, sizeof(input) );
// prep for first iteration through while loop
memset(tempWord, 0x00, sizeof(tempWord) );
// note leading ' ' in format string
// note sizing of input to avoid buffer overrun
while( 1 != fscanf(finput, " %[MAX_WORD_LEN]s", tempWord) )
{
dupFound = false;
for j = 0;j<MAX_WORDS;j++)
{
if( 0 == strcmp(tempWord, input[j].word) )
{ // then dup word found
input[j].freq++;
dupFound = true;
break;
} // end if
} // end for
if( !dupFound )
{ // then, add new word
strcpy( input[numWords].word, tempWord );
input[numWords].freq = 1;
numWords++;
} // end if
// prep for next iteration of while loop
memset(tempWord, 0x00, sizeof(tempWord) );
} // end while
The comparison should be:

How can I count specific words from a file?

I am trying to create a c program that read a file and count specific words.
I tried this code but I don't get any result:
#include<stdio.h>
#include<stdlib.h>
void main
{
File *fp = fopen("file.txt","r+");
int count =0;
char ch[10];
while((fgetc(fp)!=NULL)
{
while((fgetc(fp)!=NULL)
{
if((fgets(ch,3,fp))=="the" || (fgets(ch,3,fp))=="and")
count++;
}
}
printf("%d",count);
}
As you're acquiring data in blocks of 3 at a time, you're assuming that the two words "the" and "and" are aligned on 3 character boundaries. That will not, in general, be the case.
You also need to use strncmp to compare the strings.
As a first review, I'd read line by line and search each line for the words you want.
I'm also unsure as your intention behind having two nested while loops.
You can't compare string pointers with the equality operator, you have to use the strcmp function.
There are also other problems with the code you have. For once, the fgetc calls does not return NULL on errors or problems, but EOF. Otherwise it returns a character read from the file.
Also, your two fgets in the condition will cause reading of two "lines" (though each "line" you read will only be two characters) from the file.
fgets(ch, 3, fp) makes you read 2 characters plus the null-terminator, if you want to read 3 characters and the null-terminator you want fgets(ch, 4, fp) instead. Also, you need to use strcmp to compare strings.
Also, what are all those while loops for ?
if((fgets(ch,3,fp))=="the" || (fgets(ch,3,fp))=="and")
The above line is completely useless.
fgets(ch,3,fp) gets your word from the file to ch[10] . But you cannot compare that using == .
What I would do is use strcmp and give size 4 in fgets (never forget the \o)
You gotta use strcmp() to compare two strings. Not relational operators.
Just out of my head (perhaps not the optimal way, but should be pretty easy to read and understand):
#define WHITE_SPACE(c) ((c)==' ' || (c)=='\r' || (c)=='\n' || (c)=='\t'))
int CountWords(const char* fileName,int numOfWords,const char words[])
{
int count = 0;
FILE* fp = fopen(fileName,"rt");
fseek(fp,0,SEEK_END);
int size = ftell(fp);
fseek(fp,0,SEEK_SET);
char* buf = new char[size];
fread(buf,size,1,fp);
fclose(fp);
for (int i=0,j; i<size; i=j+1)
{
for (j=i; j<size; j++)
{
if (WHITE_SPACE(buf[j]))
break;
}
for (int n=0; n<numOfWords; n++)
{
int len = strlen(words[n]);
if (len == j-i && !memcmp(buf+i,words[n],len))
count++;
}
}
delete[] buf;
return count;
}
Please note, however, that I have not compiled nor tested it (as I said above, "out of my head")...
Take a look at String matching algorithms.
You can also find implementation examples of Boyer-Moore in github
The line
if((fgets(ch,3,fp))=="the" || (fgets(ch,3,fp))=="and")
has a couple of problems:
You can't compare string values with the == operator; you need to use the strcmp library function;
You're not comparing the same input to "the" and "and"; when the first comparison fails, you're reading the next 3 characters from input;
Life will be easier if you abstract out the input and comparison operations; at a high level, it would look something like this:
#define MAX_WORD_LENGTH 10 // or however big it needs to be
...
char word[MAX_WORD_LENGTH + 1];
...
while ( getNextWord( word, sizeof word, fp )) // will loop until getNextWord
{ // returns false (error or EOF)
if ( match( word ) )
count++;
}
The getNextWord function handles all the input; it will read characters from the input stream until it recognizes a "word" or until there's no room left in the input buffer. In this particular case, we'll assume that a "word" is simply any sequence of non-whitespace characters (meaning punctuation will be counted as part of a word). If you want to be able to recognize punctuation as well, this gets a bit harder; for example, a ' may be quoting character ('hello'), in which case it should not be part of the word, or it may be part of a contraction or a posessive (it's, Joe's), in which case it should be part of the word.
#include <ctype.h>
...
int getNextWord( char *target, size_t targetSize, FILE *fp )
{
size_t i = 0;
int c;
/**
* Read the next character from the input stream, skipping
* over any leading whitespace. We'll add each non-whitespace
* character to the target buffer until we see trailing
* whitespace or EOF.
*/
while ( (c = fgetc( fp )) != EOF && i < targetSize - 1 )
{
if ( isspace( c ) )
{
if ( i == 0 )
continue;
else
break;
}
else
{
target[i++] = c;
}
}
target[i] = 0; // add 0 terminator to string
return i > 0; // if i == 0, then we did not successfully read a word
}
The match function simply compares the input word to a list of target words, and returns "true" (1) if it sees a match. In this case, we create a list of target words with a terminating NULL entry; we just walk down the list, comparing each element to our input. If we reach the NULL entry, we didn't find a match.
#include <string.h>
...
int match( const char *word )
{
const char *targets[] = {"and", "the", NULL};
const char *t = targets;
while ( t && strcmp( t, word ))
t++;
return t != NULL; // evaluates to true if we match either "the" or "and"
}
Note that this comparison is case-sensitive; "The" will not compare equal to "the". If you want a case-insensitive comparison, you'll have to make a copy of the input string and convert it all to lowercase, and compare that copy to the target:
#include <stdlib.h>
#Include <ctype.h>
#include <string.h>
...
int match( const char *word )
{
const char *targets[] = {"and", "the", NULL};
const char *t = targets;
char *wcopy = malloc( strlen( word ) + 1 );
if ( wcopy )
{
char *w = word;
char *c = wcopy;
while ( *w )
*c++ = tolower( *w++ );
}
else
{
fprintf( stderr, "malloc failure in match: fatal error, exiting\n" );
exit(0);
}
while ( t && strcmp( t, wcopy))
t++;
free( wcopy );
return t != NULL; // evaluates to true if we match either "the" or "and"
}

Resources