File reading in c with fscanf - c

Im taking a class in c programming and I have this project where they give us a half-made project, and we need to finish it and fix some of the functions.
This project is about sort of a social network.
In this project you can send messages to other users (on the same computer for now) by writing the target user and then you enter the message. Afterwards the message is saved in a file called "messages.txt" in the same folder in this Format:
"[At]25/08/2013 [From]user1 [To]user2 [Message]hello whats up?"
"[At]Date [From]user [To]user2 [Message]any user input"
now after writing this, i go to the second user and try to read from the file with this function:
void showUnreadMessages(char* userName) // the function gets the name of the current
user that wishes to read his/hers messages
{
char msg[MAX_MESSAGE];
char toU[MAX_USER_NAME];
char fromU[MAX_USER_NAME];
char at[15];
int count = 0, flag = 0, count1 = 0;
FILE *file = fopen(MESSAGE_FILENAME, "rt"); //open the messages file
FILE *temp;
clearScreen(); //system("CLS")
if (file == NULL) //if the file didn't exict open one
{
printf("No messages\n");
flag = 1;
_flushall();
file = fopen(MESSAGE_FILENAME, "wt");
_flushall();
}
while (!feof(file) && flag == 0) //if the file did exict
{
if (count1 == 0)
{
temp = file;
}
_flushall();
fscanf(file, "[At]%s [From]%s [To]%s [Message]%s\n", at, fromU, toU, msg); //scan one line at a time
_flushall();
if (strcmp(userName, toU) == 0) //if the userNames match than its a message for the current user
{
count++;
}
count1++;
}
fclose(file);
if (count > 0 && flag == 0) //if there are messages to user
{
printf("You have %d new Messages\n", count);
_flushall();
while (!feof(temp))
{
_flushall();
fscanf(temp, "[At]%s [From]%s [To]%s [Message]%s\n", at, fromU, toU, msg); //scan one line at a time to print it for the user
_flushall();
if (strcmp(userName, toU) == 0)
{
printf("New message at %s from: %s\nStart of message: %s\n-----------------------------------------\n", at, fromU, msg);
}
}
fclose(temp);
}
else if (count == 0 && flag == 0)
{
printf("You have no Messages\n");
}
if (!file)
{
remove(MESSAGE_FILENAME);
}
PAUSE; // system("PAUSE")
}
Now when i try to read with this function, it only shows that the message is the first word in the message section on the first line...
For example For "[At]25/08/2013 [From]user1 [To]user2 [Message]hello whats up?"
the message will be "hello"
and it will be printed twice.. i dont know what to do, for some reason when i open the file and do fscanf for one time it also shows that the pointer file starts "up?[At]... (what appears on the second line)"
Please help me if you understand what i did wrong (which i know is a lot)
Thanks in advance

This part of fscanf :
"..etc. [Message]%s\n"
will only read ONE word of "Hello what's up" because %s parses for contiguous characters.
nr_fields = fscanf(file, "[At]%s [From]%s [To]%s [Message]%80c\n"
would read up to 80 characters regardless of spaces etc. in the text message. Also, the destination for %80c must be 80 characters or more!
Also, always test for number of fields found by fscanf.
Finally, fscanf works when used as directed, but it does have some subtle aspects.

One issue is that temp is pointing to a handle that is no longer valid after you call fclose(file) after the first loop. You could use fgets() to read a line and strtok() and strncpy() to split the read string.
I think it would be a good idea to encapsulate the reading in an extra function to reduce code duplication.

Related

How can I redefine stdin to point to the console after using file redirection in Unix?

I am working in Unix and I am supposed to first read in text through redirection and then ask the user to enter in a specific character and count how many times it is in the character array.
const int MAX = 8000;
int input = 1;
int i = 0;
char text[MAX], letter;
while(input != 0)
{
scanf("%c", &text[i]);
if(text[i] == '0')
input = 0;
i++;
}
printf("\n%s",text);
printf("\nEnter a letter to search for in the text: ");
scanf("%c", &letter)
Currently, I am printing the correct file through redirection, however my second scanf is being skipped. I am redirecting the file using the command: ./a.out < filename.txt.
If I try and print the character letter then it will result in nothing. It must be reading the \n from inside the empty lines of the text file still. How do I stop the scanf from reading the same text file and let me enter a letter from my keyboard in the console? As an assignment I HAVE to use the command ./a.out < filename.txt.
The freopen() function is exactly what you're looking for. You just need to use it to (re)open /dev/tty as stdin. /dev/tty is a special file referring to the terminal that started the program.
From the manual page:
The freopen() function opens the file whose name is the string pointed to by pathname and associates the stream pointed to by stream with it. The original stream (if it exists) is closed. The mode argument is used just as in the fopen() function.
[...]
The primary use of the freopen() function is to change the file associated with a standard text stream (stderr, stdin, or stdout).
Here's an example:
// ...
FILE *tty;
tty = freopen("/dev/tty", "r", stdin);
if (tty == NULL) {
perror("Unable to open terminal for reading");
exit(1);
}
printf("Enter a letter to search for in the text: ");
// Now scanf will read from the console where the process started.
scanf("%c", &letter);
By the way, your program has some issues. You could read past the end of your text array, you don't correctly terminate it with a NUL character, and you also don't check for errors. A more correct version would be:
const size_t MAX = 8000;
char text[MAX];
size_t i;
int c;
for (i = 0; i < MAX; i++)
{
c = fgetc(stdin);
if (c == EOF)
break;
text[i] = (char)c;
}
text[i] = '\0';
puts(text);
There are a few ways to do this, probably the easiest is just to open /dev/tty which is a special device referring the terminal attached to the current process. I don't recommend replacing stdin as then you will lose access to your file which is redirected there. Instead just use a different file pointer and use functions like fscanf and fgetc. Eg:
FILE *tty = fopen("/dev/tty", "r");
// fopen will return NULL if there is no attached terminal
if(NULL == tty)
{
fputs("Failed opening /dev/tty", stderr);
}
else
{
printf("\nEnter a letter to search for in the text: ");
// Read a character from the terminal
char search = fgetc(tty);
// Now you can still read from the file on stdin and search for
// your letter without needing an array (which may not be large
// enough for the whole file)
char ch;
int count = 0;
while(EOF != (ch = getchar())
{
if(ch == search)
++count;
}
printf("%d occurrences of %c\n", count, search);
}
For more information on /dev/tty and other similar special files, see: https://unix.stackexchange.com/questions/60641/linux-difference-between-dev-console-dev-tty-and-dev-tty0

Reading and printing to/from a file

I am taking a course on C and have been faced with the following task: 1. Load XCode and start a new C project. If you wish, remove
any extraneous code from the project so that you are left
with only what’s necessary to run the main function in your
project.
2. Prompt the user to enter two values-- the first a char
value of ‘D’ or ‘C’. The second value should be a floating
point value representing an amount of money.
3. As each value is entered record it to a text file that
saves it in the following format:
D, 250\n
C, 500\n
4. Test your program and examine the text file that it creates
to insure that it is in the required format.
5. Write a second program that assumes a starting balance of
$1,000.00 and outputs a completed ledger and final balance
for the account, adding or subtracting each entry from the
text file you previously created. Entries marked as a ‘C’
should be added to the account and entries marked as a ‘D’
should be debited (subtracted).
I have already created the file and am now onto step 5, I believe i know how to obtain the first character from the file to check if it is a 'c' or 'd', but after that i am not sure how to obtain the numerical value from the same line. How do I do this? This is my code so far(I am unsure what to put in the if/else if statements):
FILE *pFile = fopen("Users/Justin/Desktop/Ledger.txt", "r");
float startingBalance = 1000.00;
char action;
if(pFile != NULL)
{
while(!(feof(pFile)))
{
fgets(action, 1, pFile);
if(action == 'D' || action == 'd')
{
}
else if(action == 'C' || action == 'c')
{
}
else
printf("IO Error: Problem with file");
}
}
return 0;
}
Your file is organised in lines, so it's best to read it line-wise. The function for that is fgets, which will read a whole line of a certain maximum length into a char buffer. It keeps the terminating newline (unless the line is truncated because of the max length, but let's not deal with that right now). fgets returns the line buffer or NULL if the end of the file is reached.
Once you have a line, you must examine that line. Your lines all have the same syntax, namely
<action>, <amount>
so you could use sscanf, which isn't nice but quick and dirty. (scanfs error handling, for example, is very basic, so a good strategy is to ignore badly formatted lines altogether.)
The skeleton of your function might look like this:
int ledger(const char *fn)
{
FILE *f;
char line[80]; /* char buffer for line */
int lineno = 0; /* for error reporting */
f = fopen(fn, "r");
if (f == NULL) return -1; /* error */
while (fgets(line, sizeof(line), f)) {
char action;
double amount;
int n;
lineno++;
n = sscanf(line, " %c, %lf", &action, &amount);
if (n < 2) {
printf("Skipping badly formatted line %d\n", lineno);
continue;
}
/* Do stuff, e.g. just print */
printf("%c, %16.2f\n", action, amount);
}
fclose(f);
return 0; /* success */
}

C: I/O - Quickest /best way to read ints from file

Trying to work with C I/O currently. I have a file that only holds integers and there is only one per line.. not commas, etc.. what is the best way to read them in:
//WHILE NOT EOF
// Read a number
// Add to collection
I am creating 2 files which is working fine.. but ultimately, I want to read them both in, join them into one collection, sort them and then print them out to a new file. There's no need for you to do all that for me, but please help with the above.. here is my effort so far:
void simpleCopyInputToOutput(void);
void getSortSave(void);
int main()
{
//simpleCopyInputToOutput();
getSortSave();
system("PAUSE");
return 0;
}
void getSortSave(void)
{
FILE *fp1;
FILE *fp2;
FILE *fpMerged;
printf("Welcome. You need to input 2 sets of numbers.\n");
printf("Please input the first sequence. Press 0 to stop.\n");
if ((fp1 = fopen("C:\\seq1.txt", "w")) == NULL)
{
printf("Cannot open or create first file!\n");
exit(1);
}
int num;
int i = 1;
while (num != 0)
{
printf("Please input value # %d\n", i);
scanf("%d", &num);
if (num == 0)
{
break;
}
fprintf(fp1, "%d\n", num);
i++;
}
printf("Please input the second sequence. Press 0 to stop.\n");
if ((fp2 = fopen("C:\\seq2.txt", "w")) == NULL)
{
printf("Cannot open or create second file!\n");
exit(1);
}
num = -1;
i = 1;
while (num != 0)
{
printf("Please input value # %d\n", i);
scanf("%d", &num);
if (num == 0)
{
break;
}
fprintf(fp2, "%d\n", num);
i++;
}
fclose(fp1);
fclose(fp2);
if ((fp1 = fopen("C:\\seq1.txt", "r")) == NULL)
{
printf("Cannot open first file!\n");
exit(1);
}
//WHILE NOT EOF
// Read a number
// Add to collection
//TODO: merge ints from both files, sort and output to new file
}
I would suggest you use fgets:
char buffer[16];
while (fgets(buffer, sizeof(buffer), fp1))
{
long value = strtol(buffer, NULL, 10);
/* Use the value... */
}
/* fgets failed ro read, check why */
if (!feof(fp1))
printf("Error: %s\n", strerror(errno));
Edit: How to get the number of entries in the file: If you don't keep track of it any other way (like e.g. having the number of items being the first line), the only solution may be to read the file twice. Once to count the number of lines, and once to read the actual numbers. Use fseek or rewind after the counting to "rewind" the read pointer to the beginning of the file.
I would personally put the counting in a separate function, and also the actual reading. This way you don't have to duplicate code if you want to read from multiple files.
Your problem can be divided into three different parts: reading in two files, sorting the data, and writing the output into a file. I am assuming here that the two input files are not already sorted. If they were, the problem would be greatly simplified (google for mergesort if that is the case).
If you want to open a file for reading, you have to use "r" instead of "w" as file open mode flag. In your example code the read/write parts are somehow reversed from what you describe above. Then you should use fscanf to read formatted input from a FILE*. scanf(...) is just short for fscanf(stdin, ...). You can access the files in the following way:
FILE *fin1 = fopen("seq1.txt", "r");
FILE *fin2 = fopen("seq2.txt", "r");
FILE *fout = fopen("out.txt", "w");
if (fin1 && fin2 && fout) {
// Do whatever needs to be done with the files.
}
if (fout)
fclose(fout);
if (fin2)
fclose(fin2);
if (fin1)
fclose(fin1);
Using dynamic memory to store the integers is difficult. You need to use realloc to grow a buffer as you write more and more data into it, and finally use qsort to sort the data. Someone else can hopefully give more insight into that if needed.

In C: How do I terminate while loop when comparing string output to external file

(This is for C, Unix in tcsh.)
I'm trying to write a string to an external file (output), then compare my output to the file input. If the output exists in that file, I want to print "Record found." If the output does not exist in that file, I want to print "Record not found."
I'm using a while loop to compare output to input. I have the bit working where if the record is found, the loop terminates and prints "Record found."
I can't get the "else" part to work. Please see my comment in the code about this.
I've been reading my text, notes, and googling for 48 hours. I can't seem to fix this.
Thanks for any help.
/*This program opens a file, compares output to file input*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FILE_NAME "~/MyFiles/File1"
int main() {
FILE *fp;
char *input;
char *output;
output = "MyName";
input = "new name"; /*I get a compiler error if I don't initialize input*/
int found = 0;
/*Open file, write output ("MyName") to file in order to compare below*/
fp = fopen(FILE_NAME, "a+");
fprintf(fp, "%s\n", output);
fclose(fp);
/*Testing to see what it prints, not relevant to my question other than reopening the
file to read in and compare below*/
fp = fopen(FILE_NAME, "r");
fscanf(fp, "%s", input);
printf("\n%s", input);
while (!found) {
if (strcmp(input, "MyName") == 0) {
printf("Record found.");
found = 1;
}
/*This is the part I can't get to work. I don't know what's off.*/
else {
printf("Record not found."); /*Printing this so I can see how many times it's
checking. It never terminates. How do I get it to scan through the file ONCE and then
stop?*/
fscanf(fp, "%s", input);
found = 0; /*I thought this was my loop terminator, but it has no effect.*/
}
}
fclose(fp);
return 0;
}
found=0;
0 is falsy in c. So if at the top you are looping on
while(!found)
that will evaluate to true. Since you're not exiting the loop on EOF, then if it isn't in the file, if the output isn't in the file exactly as you're expecting it, then you'll loop forever.
In other news, you should really give some consideration to buffers and how they ought to be allocated in C.
This:
found = 0; /*I thought this was my loop terminator, but it has no effect.*/
has no effect, because found is already 0 at this point, and because your loop condition is while (!found), which means "while found is not nonzero", i.e., "while found is zero".
But that's fine. The real problem is that this:
fscanf(fp, "%s", input);
will eventually reach the end of the file, and return EOF, but you don't check its return value; so if you reach the end of the file without finding the record, then you never exit the loop.
So, you can change this:
while (!found) {
if (strcmp(input, "MyName") == 0) {
printf("Record found.");
found = 1;
}
/*This is the part I can't get to work. I don't know what's off.*/
else {
printf("Record not found."); /*Printing this so I can see how many times it's
checking. It never terminates. How do I get it to scan through the file ONCE and then
stop?*/
fscanf(fp, "%s", input);
found = 0; /*I thought this was my loop terminator, but it has no effect.*/
}
}
to this:
while (!found) {
if (strcmp(input, "MyName") == 0) {
found = 1;
} else {
if(fscanf(fp, "%s", input) == EOF) { // end-of-file (or error)
break;
}
}
}
if(found) {
printf("Record found.\n");
} else {
printf("Record not found.\n");
}
Edited to add: I just saw Mahesh's comment above, which makes a good point. You also need to change these two lines:
char *input;
input = "new name"; /*I get a compiler error if I don't initialize input*/
to something like this:
char input[1024]; /* allocate space for up to 1024 characters */
and this:
fscanf(fp, "%s", input)
to something this:
fscanf(fp, "%1023s", input) /* read at most 1023 characters */
in both places.

Weird Scanf Issue

I am trying to finish a homework program that compares a string with a text file, so the user can essentially search the text file for the search term (string) in the file. I'm getting there :)
However today I'm running into a very weird issue. When it asks for the term to search for I input the text, but it never ends. I could type all day long and it still asks for input. What weird issue(s) am I overlooking? Fresh pair of eyes might help :)
/*
ask the user for a word
convert user word to LOWER CASE
open output file
open input file
test to be sure input file is open
search for target word and keep count --> how??
print results to monitor
write results to file
close files
*/
#include<stdio.h>
#include<stdlib.h>
int main (void)
{
//declare
int i =0;
int count = 0;
/*************************************************************
working with arrays and strings
*************************************************************/
char mystring[50]; //what user puts in
char target[50]; //the word in the file we are looking for
printf("input your message ");
scanf("%s", mystring);
//printf("%s", mystring);
/*************************************************************
find file, write to it, output the string, end and close file
**************************************************************/
//define text file to use
FILE *cfile;
//name of file == file
cfile = fopen("./thanksgiving_proclamation.txt", "a");
//error handling if file does not exist
if(cfile == NULL) printf("Cannot open file");
/*************************************************************
parse through file and search for string
**************************************************************/
//convert string to lowercase
for(i = 0; i < /*strlen(mystring)*/ 500; i++)//convert to string length
{
if(target[i] >= 'A' && target[i] <='Z')
//convert char between a and z into lowercase
target[i] = target[i] + 32; //makes uppercase char
}
//compare our strings
do{
//scan through file
fscanf(cfile, "%s", mystring);
//convert string to lowercase
for(i = 0; i < /*strlen(mystring)*/ 300; i++)//convert to string length
{
if(mystring[i] >= 'A' && mystring[i] <='Z')
//convert char between a and z into lowercase
mystring[i] = mystring[i] + 32; //makes uppercase char
}
if(strcmp(mystring, target) == 0)
count++;
}while(!feof(cfile));
//while(strcmp(target,"quit")!=0)//end loop
//print to file
fprintf(cfile, "%s", mystring);
//close file
fclose(cfile);
//show user file has been written
printf("\nSuccess. File has been written\n");
printf("Press Enter to Continue...");
getchar();
return 0;
}
You open the file in append mode:
cfile = fopen("...", "a");
and then you try to read from it.
fscanf(cfile, "%s", mystring);
For a first attempt at solving the problem, I'd try to open the file for reading, read from it inside the loop and close the file. Then open it again, this time for appending to add the mystring there (and fclose it).
Once that works, if you want to, try to see if opening in "reading and appending mode" works ...
cfile = fopen("...", "a+");
You don't need "&mystring", "mystring" is already the address of the array.
It would be better to use gets or getline.
You are reading the search string into mystring, but then you are also reading the file contents into mystring.
I think pmg has hit on the actual problem; you've opened the file in append mode, and according to my copy of H&S reading from an append stream is not permitted. You'd have to open it "a+" (append/update) in order to read and write the stream.
You should always check the result of the *scanf() call (fscanf(), sscanf(), scanf(), etc.) for success before checking feof() or ferror(), and you should never make feof() the loop test condition (since it won't return true until after you've attempted to read past the end of the file, your loop will always execute once too many times).
I'd change your loop to something like this:
for(;;)
{
if (fscanf(cfile, "%s", mystring) != 1)
{
if (feof(cfile))
{
fprintf(stderr, "Reached end of file!\n");
break; // exit loop
}
if (ferror(cfile))
{
fprintf(stderr, "Error while reading from file!\n");
break;
}
}
/**
* continue as before
*/
}
It ends when you hit Enter and only stores characters till a whitespace.

Resources