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 */
}
Related
The software intends to read from a csv styled file (it's delimited by space not by a comma) and split the initial file into two new ones. The two files are determined by the last field, which is a binary value. As it stands, it currently reads the file character by character. I want it to recognize the space, and only run the individual character check on the very last field. To my understanding, strtok() will come in handy, but I'm struggling to find a way to incorporate that into the existing software.
Any help would be greatly appreciated :)
/*
* C program to parse a file, and split it into two based on the final line of input
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Function declarations */
int isMale(signed char _line);
int isFemale(signed char _line);
int isMale(signed char _line)
{
}
int isFemale(signed char _line)
{
}
int main()
{
/* File pointer to hold reference to different files */
FILE * fPtrIn, // Input file
* fPtrMale, // Males of school age
* fPtrFemale, // Females of school age
* fPtrMisc; // Data not within the given parameters
// current_char is the current character being read
// success stores the read status
char current_char;
int success;
// Open all files to perform read/write.
fPtrIn = fopen("data/example.txt", "r");
fPtrMale = fopen("data/males.txt" , "w");
fPtrFemale = fopen("data/females.txt" , "w");
fPtrMisc = fopen("data/erroneus.txt", "w");
// fopen() return NULL if unable to open file in given mode.
if(fPtrIn == NULL || fPtrMale == NULL || fPtrFemale == NULL || fPtrMisc == NULL)
{
// Unable to open file, exit software
printf("Unable to open file.\n");
printf("Please check whether file exists and you have read/write privilege.\n");
exit(EXIT_FAILURE);
}
// File open success message
printf("File opened successfully. \n\n");
// Read an integer and store read status in success.
while (fscanf(fPtrIn, "%d", ¤t_char) != -1)
{
// Write each one to separate file
if (isMale(current_char))
fprintf(fPtMale, "%d\n", current_char);
else if (isFemale(current_char))
fprintf(fPtrFemale, "%d\n", current_char);
else
fprintf(fPtrMisc, "%d\n", current_char);
}
// Done with all files, hence close all.
fclose(fPtrIn);
fclose(fPtrMale);
fclose(fPtrFemale);
fclose(fPtrMisc);
printf("Data written to files successfully.");
return 0;
}
You should
Read lines via fgets()
Copy the line read because strtok() will modify original buffer.
parse fields via strtok()
Judge and output according to the parsed field.
// hoping that too long lines won't come
char line[102400], line_parse[102400];
// Read an integer and store read status in success.
while (fgets(line, sizeof(line), fPtrIn) != NULL)
{
char *last_field, *ret;
// Copy the line for parsing
strcpy(line_parse, line);
// Separate the line into tokens
last_field = ret = strtok(line_parse, " ");
while (ret != NULL)
{
last_field = ret;
ret = strtok(NULL, " ");
}
// Get the first character of the last field
if (last_field == NULL) current_char = '\0'; else current_char = last_field[0];
// Write each one to separate file
if (isMale(current_char))
fputs(line, fPtrMale);
else if (isFemale(current_char))
fputs(line, fPtrFemale);
else
fputs(line, fPtrMisc);
}
So I want to make kind of an encryption program for a school project,I want for example the letter 'a' to be replaced with:12345 'b' with 54321 in C,how can I accomplish this?I'm not the best,my code so far:
eFile = fopen("Message.txt", "ab+");
while(!feof(eFile))
{
fscanf(eFile,"%c",&message);
}
I want for example if I write the word apple into the text file,make the program scan it letter by letter and replace every letter with a 5 digit number(I have them predefined already) example: apple = 12342 69865 69865 31238 43297
read character by character from input
use simple array to convert between character to number or to string, or use a handler function for that
print that number
replacing is not easy, simplest way is to create a tempfile, write numbers to that tempfile, than copy the tempfile to the original file and remove tempfile.
_
#include <stdio.h>
#include <assert.h>
int cipher_table[255] = {
['a'] = 12342,
['p'] = 69865,
['l'] = 31238,
['e'] = 43297,
// so on...
};
int main()
{
FILE *fin = stdin; // fopen(..., "r");
assert(fin != NULL);
FILE *fout = stdout; // tmpfile();
assert(fout != NULL);
for (int c; (c = getc(fin)) != EOF;) {
if (c == '\n') {
if (fputc('\n', fout) == EOF) {
fprintf(stderr, "Error writing to file fout with fputc\n");
return -1;
}
continue;
}
if (fprintf(fout, "%5d ", cipher_table[c]) < 0) {
fprintf(stderr, "Error writing to file fout with fprintf\n");
return -1;
}
}
close(fin);
close(fout);
return 0;
}
I'm not sure if your strategy can be called encryption, but it can be easily accomplished
with a lookup table.
Simply put the replacements in an int table like so:
int map[]={ //size will be autoinferred to fit the indices
['a']=12342,
['p']=69865,
['l']=31238,
['e']=43297,
//you should preferrably have an entry for each value of char
};
And use it to print the replacements.
int c;
while(EOF!=(c=fgetc(inputFile)))
if(0>(outPutfile,"%d \n", map[c]))
return -1;
Since the size of the new file will unpredictably change, it'd probably
be a good idea to output into a temporary file and then move it in the
place of the original after it's successfully finished.
A better idea might be to simply forget about in-place file rewriting
and simply read stdin and write to stdout – that would allow the program to handle streams well as well and a possibly wrapper script could turn it into an inplace translator (via the temp file) after the fact if needed.
So both the code to print the lines from the txt file and the code to count the lines in the txt file work fine when the other is commented out but when i try to have both work only the code that comes first works e.g. if i put the code to print out the lines first, the line count is always zero. However if i put the code to count the lines first, the number is correct but the lines from the txt file are not printed :S
#include <stdio.h>
int main(int argc, char const *argv[])
{
const int SIZE = 128;
char line[SIZE];
FILE *srcFile;
int c;
int count = 0; // Line counter (result)
if (argc == 1)
{
printf("No command line arguments given!\n");
return 1;
}
srcFile = fopen(argv[1], "r");
if (srcFile == NULL)
{
perror("\n*** FILE OPEN FAILED ***");
}
else
{
printf("\n*** FILE OPEN SUCCESSFUL ***\n\n");
}
while(fgets(line, SIZE, srcFile) != NULL)
{
printf("%s", line);
}
for (c = fgetc(srcFile); c != EOF; c = fgetc(srcFile))
{
if (c == '\n')
{
count ++;
}
}
if(c != '\n' && count != 0)
{
count ++;
}
printf("The file %s has %d lines\n ", argv[1], count);
fclose(srcFile);
return 0;
}
Here is a quick overview of how working with files is done in most programming languages:
When you open a file in a program you obtain a handle to that file. What the handle representation is depends on the language. In c it is the FILE structure. The handle contains - among other things - a file position indicator. Every read and write to that file through this handle happens at that position. Usually a read/write operation advances this file position indicator. Think about it: how do consecutive reads know to each read where the previous one left? You don't provide an argument to the read function telling it where to read from. You just "say" read. What happens is that each read call reads at the file position indicator and then advances this indicator, thus when the next read happens the handle has an updated file position indicator.
So the solution to your problem is - as mentioned in the comments - to put this position indicator to the beginning of the file. In c this can be done with rewind.
curious how the code fragment would have to look to include the line
count int the same loop as the print lines
Simple. Pseudocode:
line_count = 0
while (read line successful)
{
print line
increment line_count
}
print line_count
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.
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.