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);
}
}
Related
In the binary file mydata.dat, I've written a string: "this is a test". That's the full contents of the file. I want to read the string back but I don't see any output. The program runs without error though. Any idea what I'm doing wrong?
FILE *f = fopen("mydata.dat", "rb");
char content[100];
while(fread(content, sizeof(content), 1, f) == 1){
printf("%s", content);
}
fclose(f);
First, if you want to read characters, you should use fgets(). Let's say that you really want to use fread().
You must understand that fread() returns the number of items read, so in your case it's 0. Because you ask to fread() to read 1 element of 100 bytes... This will always return 0, if your file has less than 100 bytes. You have swapped the size of an element and the number of elements.
Plus if you want your array to be a valid C string you must put a NULL-terminator byte at the end. Because fread() will not do it for you.
Example:
#include <stdio.h>
int main(void) {
FILE *f = fopen("mydata.dat", "rb");
if (f == NULL) { // Error check
perror("fopen()");
return 1;
}
char content[100];
size_t ret;
// We loop on the file to read 99 bytes at each loop
// sizeof *content is the size of an element of content
while ((ret = fread(content, sizeof *content, sizeof content - 1, f)) > 0) {
content[ret] = '\0'; // We use ret to nul terminate our string
printf("%s", content);
fflush(stdout); // flush the standard output
}
fclose(f);
}
I'm using the fopen with fread for this:
FILE *fp;
if (fopen_s(&fp, filePath, "rb"))
{
printf("Failed to open file\n");
//exit(1);
}
fseek(fp, 0, SEEK_END);
int size = ftell(fp);
rewind(fp);
char buffer = (char)malloc(sizeof(char)*size);
if (!buffer)
{
printf("Failed to malloc\n");
//exit(1);
}
int charsTransferred = fread(buffer, 1, size, fp);
printf("charsTransferred = %d, size = %d\n", charsTransferred, strlen(buffer));
fclose(fp);
I'm not getting the file data in the new file. Here is a comparison between the original file (right) and the one that was sent over the network (left):
Any issues with my fopen calls?
EDIT: I can't do away with the null terminators, because this is a PDF. If i get rid of them the file will corrupt.
Be reassured: the way you're doing the read ensures that you're reading all the data.
you're using "rb" so even in windows you're covered against CR+LF conversions
you're computing the size all right using ftell when at the end of the file
you rewind the file
you allocate properly.
BUT you're not storing the right variable type:
char buffer = (char)malloc(sizeof(char)*size);
should be
char *buffer = malloc(size);
(that very wrong and you should correct it, but since you successfully print some data, that's not the main issue. Next time enable and read the warnings. And don't cast the return value of malloc, it's error-prone specially in your case)
Now, the displaying using printf and strlen which confuses you.
Since the file is binary, you meet a \0 somewhere, and printf prints only the start of the file. If you want to print the contents, you have to perform a loop and print each character (using charsTransferred as the limit).
That's the same for strlen which stops at the first \0 character.
The value in charsTransferred is correct.
To display the data, you could use fwrite to stdout (redirect the output or this can crash your terminal because of all the junk chars)
fwrite(buffer, 1, size, stdout);
Or loop and print only if the char is printable (I'd compare ascii codes for instance)
int charsTransferred = fread(buffer, 1, size, fp);
int i;
for (i=0;i<charsTransferred;i++)
{
char b = buffer[i];
putchar((b >= ' ') && (b < 128) ? b : "-");
if (i % 80 == 0) putchar('\n'); // optional linefeed every now and then...
}
fflush(stdout);
that code prints dashes for characters outside the standard printable ASCII-range, and the real character otherwise.
The following code writes an array of unsigned char (defined as byte) to a file:
typedef unsigned char byte;
void ToFile(byte *buffer, size_t len)
{
FILE *f = fopen("out.txt", "w");
if (f == NULL)
{
fprintf(stderr, "Error opening file!\n");
exit(EXIT_FAILURE);
}
for (int i = 0; i < len; i++)
{
fprintf(f, "%u", buffer[i]);
}
fclose(f);
}
How do I read the file back from out.txt into a buffer of byte? The goal is to iterate the buffer byte by byte. Thanks.
If you want to read it back, I wouldn't use %u to write it out. %u is going to be variable width output, so a 1 takes one character, and a 12 takes two, etc. When you read it back and see 112 you don't know if that's three characters (1, 1, 2), or two (11, 2; or 1, 12) or just one (112). If you need an ASCII file, you would use a fixed width output, such as %03u. That way each byte is always 3 characters. Then you could read in a byte at a time with fscanf("%03u", buffer[i]).
How do I read the file back from out.txt into a buffer of byte? The goal is to iterate the buffer byte by byte. Thanks.
Something similar to this should work for you. (Not debugged, doing this away from my compiler)
void FromFile(byte *buffer, size_t len)
{
FILE *fOut = fopen("out.txt", "rb");
int cOut;
int i = 0;
if (fOut == NULL)
{
fprintf(stderr, "Error opening file!\n");
exit(EXIT_FAILURE);
}
cOut = fgetc(fOut);
while(cOut != EOF)
{
buffer[i++] = cOut; //iterate buffer byte by byte
cOut = fgetc(fOut);
}
fclose(fOut);
}
You could (and should) use fread() and fwrite() (http://www.cplusplus.com/reference/cstdio/fread/) for transferring raw memory between FILE s and memory.
To determine the size of the file (to advise fread() how many bytes it should read) use fseek(f, 0, SEEK_END) (http://www.cplusplus.com/reference/cstdio/fseek/) to place the cursor to the end of the file and read its size with ftell(f) (http://www.cplusplus.com/reference/cstdio/ftell/). Don't forget to jump back to the beginning with fseek(f, 0, SEEK_SET) for the actual reading process.
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);
}
In my code below, the file is being written correctly as far as I can tell. When I look in the file floats.dat I see this stream of binary ÍÌL#33c#ÍÌÜ#ffFAßOeA^#^#bBf6zE33äCff<83>BÍ̦B
However my program always ends up triggering this if statement:
if(fread(inputFloats, sizeof(float), LENGTH, binaryFile) < LENGTH)
{
fprintf(stderr, "Problem reading some or all data from %s\n\n", binaryFileName);
return EXIT_FAILURE;
}
Does anybody see something I've done wrong here? Full code below.
#include <stdlib.h>
#include <stdio.h>
#define LENGTH 10
int main(void)
{
FILE *binaryFile, *textFile;
char *binaryFileName = "floats.dat", *textFileName = "floats.txt";
float floats[LENGTH] = {3.2, 3.55, 6.9, 12.4, 14.332, 56.5, 4003.4, 456.4, 65.7, 83.4};
float inputFloats[LENGTH];
int i;
if((binaryFile = fopen(binaryFileName, "r+")) == NULL)
{
fprintf(stderr, "Problem opening %s", binaryFileName);
}
if(fwrite(floats, sizeof(float), LENGTH, binaryFile) < LENGTH)
{
fprintf(stderr, "Problem writing some or all data to %s\n", binaryFileName);
return EXIT_FAILURE;
}
printf("DATA WRITTEN SUCCESSFULLY\n");
if(fread(inputFloats, sizeof(float), LENGTH, binaryFile) < LENGTH)
{
fprintf(stderr, "Problem reading some or all data from %s\n\n", binaryFileName);
return EXIT_FAILURE;
}
for(i = 0; i < LENGTH; i++)
{
printf("float[%d] = %f\n", i, floats[i]);
}
return EXIT_SUCCESS;
}
You're not working with text data so you should specify a binary mode when opening the file. Use r+b instead of r+
You need to fseek(binaryFile, 0, SEEK_SET) to "rewind" the file after writing. rewind can also be used for this case - fseek allows you to position the read/write pointer wherever you want.
The FILE structure keeps a record of where in the file it is currently pointing. Since you've just written to binaryFile, the file pointer is at the end of what you've written.
You therefore need to rewind the file, using fseek(binaryFile, 0, SEEK_SET); before you read.
You forgot to rewind your file before reading it:
rewind(binaryFile);
When you finish writing to the file, the FILE pointer is at the end of it, so of course if you try reading it will not work. Try using fseek to move the pointer to the beginning of the file before reading.
Please avoid this :
if((binaryFile = fopen(binaryFileName, "r+")) == NULL) {
and prefer this:
binaryFile = fopen(binaryFileName, "rb+");
if(!binaryFile) {