I have an assignment that has asked me to copy a file using buffered i/o. It has multiple requirements:
Take one parameter and an optional second
Open the first parameter for reading
Open the second for writing
If there is no second parameter make a new file called prog1.out
Use a buffer size of 20 bytes
When copying the file, print any buffer starting with the characters "rwxr"
close all opened files before exiting.
The problem I'm having is with number six, I've looked around and can't figure this out. I've tried memchr but I don't think I'm on the right track. If anyone can help push me in the right direction I'd be grateful.
This is my code:
# include <stdlib.h>
# include <stdio.h>
int main(int argc, char *argv[])
{
FILE *readfile, *writefile;
char buffer[1024];
int fileSize;
int readResult;
int writeResult;
// making sure arguments exist
if (argc < 2|| argc > 3){
printf("This program takes either 1 or 2 arguments.\n");
exit(1);
}
//Opening file for reading
readfile = fopen(argv[1], "r");
if (!readfile) {
printf("Unable to open file %s.\n", argv[1]);
exit(1);
}
//finding the file size
fseek (readfile, 0, SEEK_END);
fileSize = ftell (readfile);
fseek (readfile, 0, SEEK_SET);
// read the file
readResult = fread(buffer, 20, fileSize/20, readfile);
if (readResult == 0) {
printf("A read error occured.\n");
exit(1);
}
//check to see if there is a second parameter (argument)
if (argc == 3) {
writefile = fopen(argv[2], "w");
if (!writefile) {
printf("Unable to open file %s.\n", argv[2]);
exit(1);
}
writeResult = fwrite(buffer, 20, fileSize/20, writefile);
if (writeResult != readResult) {
printf("A write error occured.\n");
exit(1);
}
printf("File %s successfully copied to %s.\n", argv[1], argv[2]);
}
else {
writefile = fopen("program1.out", "w");
if (!writefile) {
printf("Unable to open file program1.out\n");
exit(1);
}
writeResult = fwrite(buffer, 20, fileSize/20, writefile);
if (writeResult != readResult) {
printf("A write error occured.\n");
exit(1);
}
printf("File %s successfully copied to %s.\n", argv[1], "program1.out
}
fclose(readfile);
fclose(writefile);
exit(0);
}
There's the naive way:
if(buffer[0] == 'r' && buffer[1] == 'w'
&& buffer[2] == 'x' && buffer[3] == 'r') {
//do something
}
But take a look at strncmp(), which you can use for comparing parts of a string.
remember to first check if you have read at least 4 chars into the buffer. e.g. if the file is 21 bytes long, your 2. fread might only read 1 character, and you shouldn't compare against the other 3 chars in the buffer.
If you print out the buffer with e.g. printf or puts or any other function that expects a string, the buffer needs to end with a '\0' byte, otherwise the string functions doesn't know when to stop.
I'll first answer the question you actually asked: memcmp is a good way to compare two buffers. Some caveats:
You also have to make sure the size of the buffer is at least as large as the size of the target string
memcmp returns 0 if the two buffers match, which can be counter-intuitive.
So for example, if you wanted to see if a buffer is equal to the string "rwxw", you could write
if (readresult >= strlen("rwxw") && !memcmp(buffer, "rwxw", strlen("rwxw"))) {
// buffer match occurred!
}
Personally I would use a "#define" or const char to ensure that the three places where that string appear are actually the same string. For example:
#define MATCH_STRING "rwxw"
if (readresult >= strlen(MATCH_STRING) && !memcmp(buffer, MATCH_STRING, strlen(MATCH_STRING))) {
// buffer match occurred!
}
However there are a couple of other problems with your code. One is that you need a loop that continually reads from the input file and write from the output file until the input is exhausted. For example, along the lines of
while (true) {
readResult = fread(buffer, 20, 1, readfile);
if (readResult == 0) {
// end of file
break;
}
// put your check for the "rwxr" string here!
writeResult = fwrite(buffer, readResult, 1, writefile);
if (writeResult != readREsult) {
printf("error\n");
}
}
Finally, you have what might be called a "stylistic" bug. There are two cases in your program: a specified filename and a default filename. Those two cases share a lot of code in common, but you did a cut and paste. This makes the code harder to understand, and more prone to bugs if it's changed in the future. If you are cutting-and-pasting code you're doing something wrong! Consider instead something like this, which maximizes shared code paths:
char *outFileName;
if (argc == 3) {
outFileName = argv[2];
} else {
outFileName = "prog1.out";
}
writefile = fopen(outFileName, "w");
if (!writefile) {
printf("Unable to open file %s.\n", writeFileName);
exit(1);
}
Related
I am writing a program in C to recover images from a raw file for CS50 and I am having a strange problem. I have a variable int cnt that I was using for debug purposes and I got the program to work so I was removing leftover debug code. But when I remove the cnt declaration I start outputting corrupt files.
Before removing line 25 below I was outputing .jpg files that I could open and view, then I removed the line, recompiled, deleted the photos from the last run, and reran the program on the same .raw data and the new files I got were unrecognized. So I put the declaration back in, recompiled, deleted the old photos, and ran the program again and got good files. Does anyone know why removing an unused declaration is messing with my results? The offending declaration is on line 25.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("Usage: ./recover image\n");
return 1;
}
int filesFound = 0;
FILE *inFile = fopen(argv[1], "r");
FILE *outFile = NULL;
if (inFile == NULL)
{
printf("Image file could not be opened\n");
return 1;
}
uint8_t buffer[512];
int cnt = 0;
while (!feof(inFile))
{
fread(buffer, 512, 1, inFile);
// check for start of jpg file
if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff && (buffer[3] & 0xf0) == 0xe0)
{
// start of jpg was found
if (outFile != NULL)
{
// close the current file and then open a new file to write to
fclose(outFile);
outFile = NULL;
}
// open a file to write to
char fName[4];
sprintf(fName, "%03i.jpg", filesFound);
outFile = fopen(fName, "w");
filesFound++;
}
if (outFile != NULL){
// we have found data to write and opened a file
fwrite(buffer, 512, 1, outFile);
}
}
//Be sure to close my files
fclose(inFile);
if (outFile != NULL)
{
fclose(outFile);
}
return 0;
}
char fName[4] does not have sufficient room for the name generated by "%03i.jpg", so you are overrunning the buffer. Make it larger and use snprintf, not sprintf, and test the return value to detect errors:
int result = snprintf(fName, sizeof fName, "%03i.jpg", filesFound);
if (sizeof fName <= result)
{
fprintf(stderr, "Internal error, buffer is too small for file name.\n");
exit(EXIT_FAILURE);
}
Instead of printing an error, you could instead use the return value of snprintf, which indicates the length needed, to allocate memory for a larger buffer and then redo the snprintf with that buffer.
(Note that snprintf may return a negative result if an error occurs. Normally, this will become a large number upon conversion to size_t for the comparison, so it will trigger this error message. However, in a robust program, you might want to insert a separate test for result < 0.)
I am trying to run the code. All the images are coming fine as per specification excluding the last one.
The first four bytes (B) repeating are as follows :
b8 97 98 c5
The end of file is not encountered as a result the last image is found corrupted.
EDIT:
It is already mentioned that there are 50 images in the file.
You can get the raw file from : http://cdn.cs50.net/2017/fall/psets/4/recover/card.raw
The original code is as follows :
// Recovers lost images (.jpeg) in a memory card
#include <stdio.h>
#include <stdlib.h>
#define buffsize 10
// Function to check whether jpeg or not
int check_jpeg(unsigned char *argv) {
unsigned int v1 = (int)argv[0];
unsigned int v2 = (int)argv[1];
unsigned int v3 = (int)argv[2];
unsigned int v4 = (int)argv[3];
if (v1 == 0xff && v2 == 0xd8 && v3 == 0xff) {
switch (v4) {
case 0xe0:
case 0xe1:
case 0xe2:
case 0xe3:
case 0xe4:
case 0xe5:
case 0xe6:
case 0xe7:
case 0xe9:
case 0xea:
case 0xeb:
case 0xec:
case 0xed:
case 0xee:
case 0xef:
return 1;
break;
default:
return 0;
}
} else {
return 0;
}
}
int main(int argc, char *argv[]) {
// Cautioning the user for wrong usage
if (argc != 2) {
fprintf(stderr, "Usage: ./recover file\n");
return 1;
}
// Opens the .raw file to begin inspection
FILE *camera = fopen(argv[1], "r");
// Checks the validity of the opened file
if (camera == NULL) {
fprintf(stderr, "Error opening file: %s\n",argv[1]);
return 2;
}
int counter = 0; // Declaring and Initialising the counter
int online = 0; // To know whether image is being written
char *filename = (char*)malloc(buffsize);
FILE *outptr;
while (1) {
unsigned char *image = malloc(512);
if (image == NULL) {
fprintf(stderr, "Error creating pointer \n");
return 200;
}
fread(image, 512, 1, camera);
if (image != NULL) {
int flag = check_jpeg(image);
if (counter == 50) {
printf("%x %x %x %x\n", image[0], image[1], image[2], image[3]);
}
if (flag == 1) {
if (counter != 0) {
fclose(outptr);
}
counter++;
// Creating the output file pointer
snprintf(filename, buffsize - 1, "%03i.jpg", counter);
outptr = fopen(filename, "w");
if (outptr == NULL) {
fprintf(stderr, "Error opening file: %s\n", filename);
return 201;
}
// Writing to the file
fwrite(image, 512, 1, outptr);
online = 1;
} else
if (flag == 0 && online == 1) {
fwrite(image, 512, 1, outptr); // Continue writing to the output file
}
free(image);
} else {
fclose(camera);
fclose(outptr);
return 0;
}
}
}
There are multiple issues in your code:
you do not check how much data fread successfully reads. At end of file, fread returns 0, otherwise fread returns the number of blocks successfully read into the destination array. To keep track of bytes read, pass 1 as the block size and 512 as the number of blocks.
there is no real need to allocate the filename and the input/output buffers, local arrays are fine for your purpose.
the files should be open in binary mode: FILE *camera = fopen(argv[1], "rb");
the second argument to snprintf should be the buffer size, not the maximum number of characters to write: snprintf(filename, buffsize, "%03i.jpg", counter);
fread wont set the pointer (or is at least not guaranteed to) to NULL on failed read. In fact I think it is supposed to leave the pointer unchanged. fread will however return the number of bytes read, so you could change:
fread(image, 512, 1, camera);
if (image != NULL)
to
int bytesread=fread(image, 1, 512, camera);
if (bytesread!= 512)
You might be tempted to do the following:
while (!feof(camera)) {
However, this works only in the case of there being no other errors reading the file, and even then always results in there being one additional read of the file (the one that triggers the EOF condition). That last read may return bad or point to stale data and so needs to be handled as per #chqrlie's answer and this previous question about feof().
Bottom line:
Check the number of bytes read, if it less than requested then use ferror() and feof() to isolate the cause so you can respond accordingly.
I am trying to repeatedly read a string from the command line and print it to a file. This is my code:
int main ()
{
FILE* fp=fopen("test.txt","w");
char* tofile[10];
while(1){
printf("cat: ");
scanf("%s",tofile);
fprintf(fp,"%s\n",tofile);
}
return 0;
}
It works just fine outside the loop. But inside, it just doesn't print.
The fprintf function returns the correct amount of characters it has to print.
Note: I know there's a similar question out there, but it hasn't been answered yet, and I hope my code can help in this matter since it's simpler.
Well first it doesn't seem that what you want is reading on the command line.
The command line what you write right when you execute your program such as:
./main things that are on the command line
What it seems you want to do is to read on the standard input.
What you should consider is to use the fgets function, as it has a limit of characters to be read, so that you can store them "safely" into a buffer, like your tofile.
As you want to read on the standard input you can use the stdin stream (which is a FILE* that is automatically created for every program)
The line goes
fgets(tofile, 10, stdin);
Your loop becoming :
while (fgets(tofile, 10, stdin) != NULL) {
printf("cat: ");
fprintf(fp, "%s\n", tofile);
}
meaning: as long as we can read on the standard input, print "cat :" and store what we just read in the file controlled by the stream pointer fp.
Some important stuff
When you try to open a stream it may fail and you should test it:
char filename[] = "test.txt";
FILE *fp = fopen(filename, "w");
if (fp == NULL) {
fprintf(stderr, "Failed to open the file of name : %s", filename);
return EXIT_FAILURE;
}
Right before exiting your main, you should also close the file and check if it has succeeded, like that for example:
if (fclose(fp) != 0) {
fprintf(stderr, "Failed to close the file of name : %s", filename);
return EXIT_FAILURE;
}
The whole thing becomes:
#include <stdio.h>
#include <stdlib.h>
int main (void) {
char filename[] = "test.txt";
FILE *fp = fopen(filename, "w");
if (fp == NULL) {
fprintf(stderr, "Failed to open the file of name : %s", filename);
return EXIT_FAILURE;
}
char tofile[10];
printf("cat: ");
while (fgets(tofile, 10, stdin) != NULL) {
printf("cat: ");
fprintf(fp, "%s\n", tofile);
}
if (fclose(fp) != 0) {
fprintf(stderr, "Failed to close the file of name : %s", filename);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Improvements
I don't know if it is just a little program or it aspires to become a greater program.
In the last case you should consider using defines and not a magical number such as
#define BUFFER_MAX_SIZE 10
char tofile[BUFFER_MAX_SIZE];
while (fgets(tofile, BUFFER_MAX_SIZE, stdin) != NULL) { ... }
This helps for readability and makes the program less apt to debug when modifying such a size. Because with the define all the part of the code needing the size will still be fully functional without modifying them.
Please also keep in mind that your tofile acts as a buffer, and it's really a small buffer that can easily be overflowed.
This will work. fgets() returns the string it reads from the specified file pointer. If this string returns only a newline ("\n"), that means nothing was entered at stdin.
#include <stdio.h>
#include <string.h>
int main(void)
{
FILE *fp = fopen("test.txt","w");
// always check if fopen() == null
if (!fp) {
fprintf(stderr, "Could not write to file\n");
return 1;
}
char tofile[30];
printf("cat: ");
while (fgets(tofile, 30, stdin)) {
if (strcmp(tofile, "\n") == 0)
break;
fprintf(fp, "%s", tofile);
printf("cat: ");
}
// always fclose()
fclose(fp);
return 0;
}
Edited code.
I'm trying to read in 256 bytes into a buffer from a 65536 byte file, treating it as a random-access file by using fopen, fread, fwrite, and fseek. I'm not getting any errors, but the buffer is still empty after the read, even though the file is non-empty and fread reports reading 256 bytes. Here's my code:
FILE *file = NULL;
char buffer[255];
memset(buffer, 0, sizeof(buffer));
file = fopen("BACKING_STORE.bin","r");
if(file == NULL) {
printf("Error: can't open file.\n");
return NULL;
} // end if
if(fread(buffer, 1, 256, file) != 256) {
printf("Error: reading from file failed.\n");
return NULL;
} // end if
printf("The bytes read are [%s]\n", buffer);
fclose(file);
And just to confirm, I opened up the BACKING_STORE.bin file in a hex editor just to make sure that it wasn't empty. Here's a screenshot of that:
After running this program, I get the output: "The bytes read are []" but without any errors.
I'm fairly new to C, so I'm sure it's just something simple I'm missing.
Thanks for the help. :)
Because you can't output binary data with "%s" and printf(). If you want to see the contents you can write a loop and print the hex value of each byte, like this
for (size_t i = 0 ; i < 256 ; ++i) {
fprintf(stdout, "0x%02x ", buffer[i]);
if ((i + 1) % 8 == 0) {
fputc('\n', stdout);
}
}
I have been tinkering around for days with a program for a class of mine, and I cannot get fwrite to cooperate. I've tried looking at the online resources for fread and fwrite, looked at my professor's examples, and tried time and time again to fix this, but nothing has worked. No matter what I do, fwrite makes it so that my text editor can't detect any kind of character encoding, so I'm presuming that fwrite is either writing memory addresses or garbage values to the file, making it so that I can't read it. The program is simply supposed to write the contents of one file into another.
Here is my code.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[]) {
//initialize files
FILE* infile, *outfile;
char* buffer;
int read = 0;
//handle the case of having the wrong number of inputs.
if (argc != 4){
printf("Error: incorrect number of inputs\n");
//returning 1 will tell us that this was an error (as opposed to returning zero)
return 1;
}
else{
//handle the case of not having read acces to file 1
if ( access(argv[1], R_OK) == 1){
printf("Error: you do not have read access to the first file\n");
return 1;
}
else {
//handle the case of not having write access to file 2
if ( access(argv[2], W_OK) == 1){
printf("Error: you do not have write access to the second file\n");
return 1;
}
else{
//handle a bad buffer size (zero or negative number)
if ((int)*argv[3] < 0){
printf("Error: bad input for buffer size.\nBuffer size: %d \n", (int)*argv[3]);
return 1;
}
}
}
//open the files in the correct mode.
infile = fopen(argv[1], "r");
outfile = fopen(argv[2], "w");
buffer = malloc((int)*argv[3]);
while (!feof(infile)){
read = fread (buffer,1,(int)*argv[3],infile);
fwrite(buffer,1,(int)*argv[3],outfile);
}
}
//close files, and deallocate the buffer.
fclose(infile);
fclose(outfile);
free(buffer);
//if we made it here, then that means that our program ran correctly, so return zero.
return 0;
}
This is wrong
(int)*argv[3]
change it to
atoi(argv[3])
and it would be better if you store the value somewhere, and check that it's convertible to integer, something like
int size;
char *endptr;
size = strtol(argv[3], &endptr, 10);
if (*endptr != '\0')
errorNotAnIntegerAbortHere();
buffer = malloc(size);
.
.
.
not that, *argv[3] would be equivalent to argv[3][0] which is just the first character in argv[3].
fread will return less than the no. of requested bytes at EOF.
Change to
if (read)
fwrite(buffer,1,read,outfile);