This question already has answers here:
Why is “while( !feof(file) )” always wrong?
(5 answers)
Closed 2 years ago.
I have this code that reads in values from a .txt file and returns the strings ending in ed.
It compiles and runs, but I can't figure out why it returns the last word twice. I thought it was the FEOF statement. Any hints or clues?
The program is supposed to prompt the user for the name of a text file (if the file does not exist, display an error message and re-prompt), this works.
Read in a series of strings from the text file and store these in an array of strings, dynamically allocating memory for each element, this works.
Loop through the populated string array and find elements that end with the characters "ed".
Display on-screen a list of only those strings that end with "ed", works but duplicates the last word.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZES_1 20 // preprpcessor directive for size of the filename array
#define SIZES_2 500 // preprocessor directive for size of the word array
// the file name needs to be a constant so that it is not changed
int readInData (const char *fileName, char* array[] ); // function protype with arguments making pointer to char that is a constant called filename,
// with pointer to an char array
// function main
int main(void){
char fileName[SIZES_1];
char *array[SIZES_2];
int i; // loop counter variable
int length; // length of current string
int N; //Size of filled array
int userPrompt=0; // used for signaling if file can be read correctly , default value is set to zero
// while loop to be used to as a signal with userPrompt to keep prompting user fro filename Prompt the user for the name of a text file
//(if the file does not exist, display an error message and re-prompt)
while (userPrompt==0){
puts("Please Input the Text file name (Max 20 characters): ");
scanf("%s", fileName);
if (-1 == (N = readInData(fileName,array))){// perform error check and if non pass to function
printf("File error try again.\n");
userPrompt=0; // keeps signal flag at 0
}
else
{
userPrompt=1;// sets flag to 1 and loop is exited
}
}// end of signal while loop
printf("\n the Following Strings end with ed: \n ");
// loop through
for (i = 0; i <= N; i++){
length = strlen(&array[i][0]); // strlen is used to f9ind the length of the string, address of array is used after trial error, array is a pointer start at i index 0
// compare and
if (strcmp(&array[i][length -2], "ed")==0){// compare the value at memory location and if last 2 letters are set return value to 0 then it is equal
printf("%s\n", &array[i][0]);// print the value at memory location
}
}
//free((array)); //deallocate memory from function malloc got error mesages until i used the double brackets
return 0;
}
int readInData (const char *fileName, char* array[] ){
char lineLth[SIZES_2];
int i =0;
int length;
// array for line length at 500 using defied size above.
FILE * cfPtr; // pointer to file stream called cfPtrlist
cfPtr = fopen(fileName,"r");// open file passed as argument to function to read only
if(!cfPtr) // i encapsulated in a bracket here and it threw an error
return -1; // return an error message to locate where the problem is i am useing this to see if file opening is still a problem
while (!feof(cfPtr)){ // while end of file signal is not reached
fscanf(cfPtr,"%s",lineLth);// scan in each line
length = strlen(lineLth);// find the length of the line ie the word
array[i] = (char*)malloc(length+1); //use +1 to allocate memory effectivly
strcpy(array[i], lineLth); // copy the string in the line ltth and pass
++i; // increment counter
}
fclose(cfPtr); // close file
return i;// return value
}
You output lineLth whether or not the call to fscanf succeeds. You need to process data only if you were successfully able to read data in. You can't use feof to predict whether or not a future operation will succeed. It's a status-reporting function that tells you about what worked in the past, not a prediction operation that tells you what will work in the future.
Related
I'm just trying to get fscanf to read all the characters in a file, along with all the words, but whenever I try to run a whileloop on the file I opened twice it doesn't seem to work? It only seems to be able to use fscanfon a file one time. I found a work around where I can scan the same file twice except I need to open the file a 2nd time for this to work. How can I use fscanf on the same instance of a file twice?
/*Description: Program will open a file named Story.txt
then counts the number of words and characters
and prints them out */
int main(){
char word[225]; // will be used to hold words (array of chars)
char c; // will be used to hold chars
int wordCount = 0; // will be used to hold the number of words in the file
int charCount = 0; // will be used to hold the number of chars in the file
FILE* wordFile = fopen("Story.txt","r"); // opens the file Story.txt for counting words
printf("Words: "); // indicates that the following outout will be the words of the file
while(fscanf(wordFile,"%s",&word)==1){ // a loop to scan the file for all the words in it until the end of the file
printf(" %s ",word); // prints out the word in the given cycle
wordCount = wordCount + 1; // keeps count of the words the loop has scanned up till now
}
fclose(wordFile); // closes wordFile and frees the memory
FILE* charFile = fopen("Story.txt","r"); // opens file Story.txt for counting chars
printf("\n\nChars: "); // indicates the following output will be the chars of the file
while(fscanf(charFile,"%c",&c)==1){ // a loop to scan the file for all the chars in it until the end fot he file
if(c!=' '){ // will check if the char is a space, if it is it will not count it
printf(" %c ",c); // prints out the char for the given cycle
charCount = charCount + 1; // keeps count of the chars the loop has scanned up till now
}
}
fclose(charFile); // closes charFile and frees the memory
printf("\n\nWord count: %d and Char count: %d",wordCount,charCount-1); // prints out the word count and char count
return 0;
}
As you can see i have to create two instances of the file or else it will not work. The first instance is called wordFile and the 2nd instance is called charFile. Here's the thing though: Both loops work, it's just that I can't use them on the same file twice. How can I make it so that I will only need to open one instance of the file and then use it to count both the words and the chars in it?
Things I tried: adding the space as suggested here didn't work: C: Multiple scanf's, when I enter in a value for one scanf it skips the second scanf (i searched fscanf but scanf is all that came up so i went off of that).
Another work around that I found strange: if i use wordFilein the 2nd while loop to search for chars it works, the only problem is I have to declare fclose(wordFile); right before it's used in the while loop. I thought fclosewas suppose to close the file and make it unusable? Anyways that worked but what I really want is to use use one instance of the file to read all the chars and strings in it.
Do something like below - the lazy way of course
char word[225];
char c;
int wordCount = 0;
int charCount = 0;
FILE* wordFile = fopen("Story.txt","r");
printf("Words: ");
while(fscanf(wordFile,"%s",word)==1){ // word gives the address not &word
wordCount = wordCount + 1;
}
printf("%d\n",wordCount);
fseek(wordFile,0,SEEK_SET); // setting the file pointer to point to the beginning of file
printf("Chars: ");
while(fscanf(wordFile,"%c",&c)==1){
if(c!=' '&& c!='\n' && c!='\t'){
charCount = charCount + 1;
}
}
printf("%d\n",charCount);
fclose(wordFile); // Closing the file once for all
return 0;
fscanf(wordFile,"%s",&word);
The variable "word" is the pointer to the first element of your array (string), so here you are scanning the address not the value. You should rather use:
fscanf(wordFile,"%s",word);
I'm try to get my text to be read back to front and to be printed in the reverse order in that file, but my for loop doesn't seem to working. Also my while loop is counting 999 characters even though it should be 800 and something (can't remember exactly), I think it might be because there is an empty line between the two paragraphs but then again there are no characters there.
Here is my code for the two loops -:
/*Reversing the file*/
char please;
char work[800];
int r, count, characters3;
characters3 = 0;
count = 0;
r = 0;
fgets(work, 800, outputfile);
while (work[count] != NULL)
{
characters3++;
count++;
}
printf("The number of characters to be copied is-: %d", characters3);
for (characters3; characters3 >= 0; characters3--)
{
please = work[characters3];
work[r] = please;
r++;
}
fprintf(outputfile, "%s", work);
/*Closing all the file streams*/
fclose(firstfile);
fclose(secondfile);
fclose(outputfile);
/*Message to direct the user to where the files are*/
printf("\n Merged the first and second files into the output file
and reversed it! \n Check the outputfile text inside the Debug folder!");
There are a couple of huge conceptual flaws in your code.
The very first one is that you state that it "doesn't seem to [be] working" without saying why you think so. Just running your code reveals what the problem is: you do not get any output at all.
Here is why. You reverse your string, and so the terminating zero comes at the start of the new string. You then print that string – and it ends immediately at the first character.
Fix this by decreasing the start of the loop in characters3.
Next, why not print a few intermediate results? That way you can see what's happening.
string: [This is a test.
]
The number of characters to be copied is-: 15
result: [
.tset aa test.
]
Hey look, there seems to be a problem with the carriage return (it ends up at the start of the line), which is exactly what should happen – after all, it is part of the string – but more likely not what you intend to do.
Apart from that, you can clearly see that the reversing itself is not correct!
The problem now is that you are reading and writing from the same string:
please = work[characters3];
work[r] = please;
You write the character at the end into position #0, decrease the end and increase the start, and repeat until done. So, the second half of reading/writing starts copying the end characters back from the start into the end half again!
Two possible fixes: 1. read from one string and write to a new one, or 2. adjust the loop so it stops copying after 'half' is done (since you are doing two swaps per iteration, you only need to loop half the number of characters).
You also need to think more about what swapping means. As it is, your code overwrites a character in the string. To correctly swap two characters, you need to save one first in a temporary variable.
void reverse (FILE *f)
{
char please, why;
char work[800];
int r, count, characters3;
characters3 = 0;
count = 0;
r = 0;
fgets(work, 800, f);
printf ("string: [%s]\n", work);
while (work[count] != 0)
{
characters3++;
count++;
}
characters3--; /* do not count last zero */
characters3--; /* do not count the return */
printf("The number of characters to be copied is-: %d\n", characters3);
for (characters3; characters3 >= (count>>1); characters3--)
{
please = work[characters3];
why = work[r];
work[r] = please;
work[characters3] = why;
r++;
}
printf ("result: [%s]\n", work);
}
As a final note: you do not need to 'manually' count the number of characters, there is a function for that. All that's needed instead of the count loop is this;
characters3 = strlen(work);
Here's a complete and heavily commented function that will take in a filename to an existing file, open it, then reverse the file character-by-character. Several improvements/extensions could include:
Add an argument to adjust the maximum buffer size allowed.
Dynamically increase the buffer size as the input file exceeds the original memory.
Add a strategy for recovering the original contents if something goes wrong when writing the reversed characters back to the file.
// naming convention of l_ for local variable and p_ for pointers
// Returns 1 on success and 0 on failure
int reverse_file(char *filename) {
FILE *p_file = NULL;
// r+ enables read & write, preserves contents, starts pointer p_file at beginning of file, and will not create a
// new file if one doesn't exist. Consider a nested fopen(filename, "w+") if creation of a new file is desired.
p_file = fopen(filename, "r+");
// Exit with failure value if file was not opened successfully
if(p_file == NULL) {
perror("reverse_file() failed to open file.");
fclose(p_file);
return 0;
}
// Assumes entire file contents can be held in volatile memory using a buffer of size l_buffer_size * sizeof(char)
uint32_t l_buffer_size = 1024;
char l_buffer[l_buffer_size]; // buffer type is char to match fgetc() return type of int
// Cursor for moving within the l_buffer
int64_t l_buffer_cursor = 0;
// Temporary storage for current char from file
// fgetc() returns the character read as an unsigned char cast to an int or EOF on end of file or error.
int l_temp;
for (l_buffer_cursor = 0; (l_temp = fgetc(p_file)) != EOF; ++l_buffer_cursor) {
// Store the current char into our buffer in the original order from the file
l_buffer[l_buffer_cursor] = (char)l_temp; // explicitly typecast l_temp back down to signed char
// Verify our assumption that the file can completely fit in volatile memory <= l_buffer_size * sizeof(char)
// is still valid. Return an error otherwise.
if (l_buffer_cursor >= l_buffer_size) {
fprintf(stderr, "reverse_file() in memory buffer size of %u char exceeded. %s is too large.\n",
l_buffer_size, filename);
fclose(p_file);
return 0;
}
}
// At the conclusion of the for loop, l_buffer contains a copy of the file in memory and l_buffer_cursor points
// to the index 1 past the final char read in from the file. Thus, ensure the final char in the file is a
// terminating symbol and decrement l_buffer_cursor by 1 before proceeding.
fputc('\0', p_file);
--l_buffer_cursor;
// To reverse the file contents, reset the p_file cursor to the beginning of the file then write data to the file by
// reading from l_buffer in reverse order by decrementing l_buffer_cursor.
// NOTE: A less verbose/safe alternative to fseek is: rewind(p_file);
if ( fseek(p_file, 0, SEEK_SET) != 0 ) {
return 0;
}
for (l_temp = 0; l_buffer_cursor >= 0; --l_buffer_cursor) {
l_temp = fputc(l_buffer[l_buffer_cursor], p_file); // write buffered char to the file, advance f_open pointer
if (l_temp == EOF) {
fprintf(stderr, "reverse_file() failed to write %c at index %lu back to the file %s.\n",
l_buffer[l_buffer_cursor], l_buffer_cursor, filename);
}
}
fclose(p_file);
return 1;
}
I found this piece of code at Reading a file character by character in C and it compiles and is what I wish to use. My problem that I cannot get the call to it working properly. The code is as follows:
char *readFile(char *fileName)
{
FILE *file = fopen(fileName, "r");
char *code;
size_t n = 0;
int c;
if (file == NULL)
return NULL; //could not open file
code = malloc(1500);
while ((c = fgetc(file)) != EOF)
{
code[n++] = (char) c;
}
code[n] = '\0';
return code;
}
I am not sure of how to call it. Currently I am using the following code to call it:
.....
char * rly1f[1500];
char * RLY1F; // This is the Input File Name
rly1f[0] = readFile(RLY1F);
if (rly1f[0] == NULL) {
printf ("NULL array); exit;
}
int n = 0;
while (n++ < 1000) {
printf ("%c", rly1f[n]);
}
.....
How do I call the readFile function such that I have an array (rly1f) which is not NULL? The file RLY1F exists and has data in it. I have successfully opened it previously using 'in line code' not a function.
Thanks
The error you're experiencing is that you forgot to pass a valid filename. So either the program crashes, or fopen tries to open a trashed name and returns NULL
char * RLY1F; // This is not initialized!
RLY1F = "my_file.txt"; // initialize it!
The next problem you'll have will be in your loop to print the characters.
You have defined an array of pointers char * rly1f[1500];
You read 1 file and store it in the first pointer of the array rly1f[0]
But when you display it you display the pointer values as characters which is not what you want. You should just do:
while (n < 1000) {
printf ("%c", rly1f[0][n]);
n++;
}
note: that would not crash but would print trash if the file read is shorter than 1000.
(BLUEPIXY suggested the post-incrementation fix for n BTW or first character is skipped)
So do it more simply since your string is nul-terminated, pass the array to puts:
puts(rly1f[0]);
EDIT: you have a problem when reading your file too. You malloc 1500 bytes, but you read the file fully. If the file is bigger than 1500 bytes, you get buffer overflow.
You have to compute the length of the file before allocating the memory. For instance like this (using stat would be a better alternative maybe):
char *readFile(char *fileName, unsigned int *size) {
...
fseek(file,0,SEEK_END); // set pos to end of file
*size = ftell(file); // get pos, i.e. size
rewind(file); // set pos to 0
code = malloc(*size+1); // allocate the proper size plus one
notice the extra parameter which allows you to return the size as well as the file data.
Note: on windows systems, text files use \r\n (CRLF) to delimit lines, so the allocated size will be higher than the number of characters read if you use text mode (\r\n are converted to \n so there are less chars in your buffer: you could consider a realloc once you know the exact size to shave off the unused allocated space).
I looked through some "FGETS" questions before posting, and what i gathered is it may be a new line character thats causing the issue for the manual input.
int main ( int argc, char *argv[] ){
char temp[1000];
FILE *user_file;
printf("Starting....\n"); //Used for user visual.
if(argc == 2){ //open file
user_file = fopen(argv[1],"r");
if( user_file == NULL ){
printf("No file was found.");
exit(2);
}else{
fgets(temp,strlen(temp),user_file);
}
}else if( argc > 2 ){ // Will exit if arguments are greater than 2.
printf("Maximum args 2.\n");
exit(1);
}else{
printf("File was not provided, please enter the text to convert.\n"); //If the user doesnt provide a file allow manual input.
fgets(temp,strlen(temp),stdin);
}
printf("%s\n",temp);
return 0;
}//End main
Questions:
Why is fgets not opening the txt file I provide it with on the cmd line, and storing it to the temp array?
Why is Fgets being skipped over in the "else" statment if the file is not provided?
Why is print being skipped over in both instances?
Hey and by the way thank you very much for the assistance.
If you know a similar question that has been asked, can you post it in the comments so I can read it.
Your code has multiple problems.
Here's the first problem:
char temp[1000];
Your buffer declaration does not initialize the buffer's contents - so the value of each char value will be whatever was in the raw memory previously. In C most strings are "null-terminated" so having a terminating NULL (0 - zero) is important otherwise you can run into buffer-overruns.
The "best" approach is to zero-out (zero-initialize) the array/buffer before you use it, like so (in C99):
char temp[1000] = {0};
...this way temp will contain all 0 values (NULL) so anything written to it (provided it's no longer than 999 bytes) will automatically have a null-terminator (though fgets will append a terminating 0 value, but not every function in C does this).
The second problem is related to the first: you're using the runtime string-length function strlen to get the size of the strlen buffer. This is incorrect as the buffer sized is fixed at compile-time to 1000. The strlen will return the index of the first 0 (NULL) char value, which is undefined behavior at this point because you haven't zero-initialized the buffer anyway (so it could return 0 immediately if the buffer's original raw data contained a zero, or it could overrun 1000 because there was never any zero value.
...thus you need to re-use the buffer-length, like so:
#define TEMP_LENGTH 1000
char temp[ TEMP_LENGTH ];
...
fgets( temp, TEMP_LENGTH, user_file );
Finally, you're making the same mistake when you call fgets( temp, ..., stdin ).
The array temp[] is uninitialized, and you attempt to find strlen(temp). You don't even know if there is a NUL stored in the array. Try doing:
#define MAXLINE 1000
and changing your calls to fgets():
fgets(temp, MAXLINE, user_file);
...
fgets(temp, MAXLINE, stdin);
Here problem is in your code, instead of passed numerical value in second argument you passed strlen(temp).
fgets(temp,strlen(temp),user_file);
right way is :-
fgets(temp,1000,user_file);
How do i read some 5 to 10 characters from a sample txt file using an fread funtion.
I have the following code:
#include <stdio.h>
main()
{
char ch,fname[20];
FILE *fp;
printf("enter the name of the file:\t");
gets(fname);
fp=fopen(fname,"r");
while(fread(&ch,1,1,fp)!=0)
fwrite(&ch,1,1,stdout);
fclose(fp);
}
when i enter any sample filename..it prints all the data of the file.
my question is how to print only the first 5 to 10 characters from the sample file.
Your while loop runs until read reaches the end of the file (reads 0 bytes for the first time).
You will want to change the condition by using a for loop or a counter.
i.e. (these are suggestions, not the full working code):
int counter = 10;
while(fread(&ch,1,1,fp)!=0 && --counter)
fwrite(&ch,1,1,stdout);
or
int i;
for(i=0; i < 10 && fread(&ch,1,1,fp) > 0 ; i++)
fwrite(&ch,1,1,stdout);
Good luck!
P.S.
To answer your question in the comments, fread allows us to read the data in "atomic units", so that if a whole unit isn't available, no data will be read.
A single byte is the smallest unit (1), and you are reading one unite (of a single byte), this is the 1,1 part in the fread(&ch,1,1,fp).
You could read 10 units using fread(&ch,1,10,fp) or read all the bytes unrequited for a single binary int (this won't be portable - it's just a demo) using int i; fread(&i,sizeof(int),1,fp);
read more here.
Here is a modified version of your code. Check the comments at the lines that are modified
#include <stdio.h>
#define N_CHARS 10 // define the desired buffer size once for code maintenability
int main() // main function should return int
{
char ch[N_CHARS + 1], fname[20]; // create a buffer with enough size for N_CHARS chars and the null terminating char
FILE *fp;
printf("enter the name of the file:\t");
scanf("%20s", fname); // get a string with max 20 chars from stdin
fp=fopen(fname,"r");
if (fread(ch,1,N_CHARS,fp)==N_CHARS) { // check that the desired number of chars was read
ch[N_CHARS] = '\0'; // null terminate before printing
puts(ch); // print a string to stdout and a line feed after
}
fclose(fp);
}