I am trying to read from a Binary file called "binary.bin" which has the content of "this". I was expecting it to give me the ASCII values of "t", "h", "i", "s" respectively, but it's giving me 5 zeroes.
void bin_byte_by_byte(char *filename) {
FILE *fptr;
unsigned long len;
int *buffer;
fptr = fopen(filename, "rb");
if(!fptr) {
printf("error: file does not exist");
return;
}
// get file lenght - create a function to this
fseek(fptr, 0, SEEK_END);
len = ftell(fptr);
fseek(fptr, 0, SEEK_SET);
buffer = (int*)malloc(sizeof(int) * len);
if(!buffer) {
printf("error: unable to allocate memory");
fclose(fptr);
return;
}
fread(&buffer, sizeof(buffer), len, fptr);
printf("len = %d\n", len);
for(int i = 0; i < len; i++) {
printf("%d ", buffer[i]);
}
if(fclose(fptr) != 0) {
printf("File did not close as expected");
}
free(buffer);
}
Your file is supposed to be binary but it seems that you pass a text file to your program. The file is 5 bytes which suits to the content "this". If you read this file as binary, maybe it makes sense to read bytes and not ints. If you want to read bytes into an int array, you should read byte-wise and store each byte into one position of your int array.
In the program you've listed there are few mistakes.
buffer = (int*)malloc(sizeof(int) * len);
the line above creates an array of 5 ints. So, it takes 20 bytes (assuming 32-bit platform).
Then you read from the file:
fread(&buffer, sizeof(buffer), len, fptr);
This line reads 20 bytes from the file although it is only 5 bytes long. Also, you pass address of the pointer variable but you need to pass the address of the buffer. So, it should be just buffer and not &buffer
But the main point is here that into buffer[0] goes 4 bytes. So, 't', 'h', 'i', 's' go to the first element of buffer.
So, either you can use char for the array type or you read byte-by-byte and store each byte into a separate element of the buffer
Related
I am trying to compare 2 texts from files byte by byte using memcmp, after I read both of them into memory, one file into a buffer(char* or char[], tried both). the problem is, the file I read into a buffer have a lot of 0 bytes, which makes him stop at the first 0 byte thinking it is a null terminating zero, which makes a segmentation fault. how can I make the function keep compare bytes even so there are 0 bytes?
I already tried to check if the buffer is full or not, I printed it byte by byte and it showed all of the bytes including the 0 bytes. when I print it completely using printf("%s", buffer) I get only the first byte(the second byte is 0 byte).
void detect_virus(char *buffer, unsigned int size){
link* l = (link*) malloc(sizeof(link));
load(l);
unsigned int location = 0;
while(l != NULL){
location = 0;
while(location < size - l->vir->SigSize){
int isVirus = memcmp(buffer + location, l->vir->sig, l->vir->SigSize);
if(isVirus == 0)
printf("%d, %s, %d\n", location, l->vir->virusName, l->vir->SigSize);
location++;
}
}
free(l);
}
void detect(link* list){
char filename[50];
fgets(filename, 50, stdin);
sscanf(filename, "%s", filename);
FILE* file = fopen(filename, "rb");
char* buffer = (char*) malloc(10000);
fseek(file, 0, SEEK_END);
unsigned int size = ftell(file);
fseek(file, 0, SEEK_SET);
fread(buffer, 1, size, file);
detect_virus(buffer, size);
fclose(file);
}
I get a segmentation fault at the first time the memcmp function is called, instead of fully compare the texts. any ideas how to fix that?
edit
code for load function:
void load(link* list){
printf("Enter Viruses file name: \n");
char* filename = (char*) malloc(100);
fgets(filename, 100, stdin);
sscanf(filename, "%s", filename);
FILE* file = fopen(filename, "r");
while(!feof(file)){
short length = 0;
fread(&length, 2, 1, file);
if(length == 0)
break;
struct virus* v = (struct virus*)malloc(length);
fseek(file, -2, SEEK_CUR);
fread(v, length, 1, file);
v->SigSize = v->SigSize - 18;
list_append(list, v);
}
list = list->nextVirus;
free(filename);
fclose(file);
}
as a note, I tested the function before and it worked.
edit
I found out the problem, thank you all!
Per 7.21.6.7 The sscanf function, paragraph 2 of the C standard (bolding mine):
The sscanf function is equivalent to fscanf, except that input is obtained from a string (specified by the argument s) rather than from a stream. Reaching the end of the string is equivalent to encountering end-of-file for the fscanf function. If copying takes place between objects that overlap, the behavior is undefined.
Note the bolded portion.
In your code:
sscanf(filename, "%s", filename);
the filename array certainly overlaps with the filename array, thus invoking undefined behavior.
Remove that line of code.
You also need to add error checking, especially checking that the return from fopen() is not NULL.
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.
For example, if I had a file name random.txt, which reads:
This is a string.
Abc
Zxy
How would you save the characters in random.txt to a string or array that includes all of the characters in the text file?
So far I have (using redirection for file)
#include <stdio.h>
#include <string.h>
int main () {
int c;
do {
c = fgetc(stdin);
putchar(c);
} while (c != EOF);
return 0;
}
First part: About file handles
The stdin variable holds a FILE handle to which the user input is redirected (the FILE data type is defined in stdio.h). You can create handles to files using the function FILE *fopen(const char *path, const char *mode).
Your example applied to a regular file would be something like this (no error checking is done):
int main() {
int c;
FILE *myfile = fopen("path/to/file", "r"); //Open file for reading
while(!feof(myfile)) {
c = fgetc(myfile);
//do stuff with 'c'
//...
}
fclose(myfile); //close the file
return 0;
}
More information about fopen here: http://linux.die.net/man/3/fopen
Second part: About C strings
C strings (char arrays terminated with the null character '\0') can be defined in several ways. One of them is by statically defining them:
char mystring[256]; //This defines an array of 256 bytes (255 characters plus end null)
It is very important to take care about the limits of the buffer. In our example, writing beyond 256 bytes in the buffer will make the program crash. If we assume our file will not have lines longer than 255 characters (including line terminators like \r and \n) we can use the fgets function (http://linux.die.net/man/3/fgets):
char *fgets(char *s, int size, FILE *stream);
Simple (newbie) example:
int main() {
char mystring[256];
FILE *myfile = fopen("path/to/file", "r"); //Open file for reading
while(!feof(myfile)) {
if (fgets(mystring, sizeof(mystring), myfile) != NULL) {
printf("%s", mystring);
}
}
fclose(myfile); //close the file
return 0;
}
Notice that fgets is used for reading lines. If you want to read characters 1 by 1, you should keep using fgetc and pushing them manually into a buffer.
Finally, if you want to read a whole text file into a C string (no error checking):
int main() {
FILE *myfile = fopen("path/to/file", "r"); //Open file for reading
//Get the file size
fseek(myfile, 0, SEEK_END);
long filesize = ftell(myfile);
fseek(myfile, 0, SEEK_SET);
//Allocate buffer dynamically (not statically as in previous examples)
//We are reserving 'filesize' bytes plus the end null required in all C strings.
char *mystring = malloc(filesize + 1); //'malloc' is defined in stdlib.h
fread(mystring, filesize, 1, myfile); //read all file
mystring[filesize] = 0; //write the end null
fclose(myfile); //close file
printf("%s", mystring); //dump contents to screen
free(mystring); //deallocate buffer. 'mystring' is no longer usable.
return 0;
}
The following will work on either stdin or an actual file (i.e. you can replace "stream" with stdin, or fopen() it with a filename), dynamically allocating as it goes. After it runs, "ptr" will be a pointer to an array holding the contents of the file.
/* read chars from stream in blocks of 4096 bytes,
dynamically allocating until eof */
size_t bytes_read = 0;
char * ptr = NULL;
while (1) {
size_t chunk_read;
/* increase size of allocation by 4096 bytes */
ptr = realloc(ptr, bytes_read + 4096);
/* read up to 4096 bytes to the newest portion of allocation */
chunk_read = fread(ptr + bytes_read, 1, 4096, stream);
bytes_read += chunk_read;
/* if fread() got less than the full amount of characters, break */
if (chunk_read < 4096) break;
}
/* resize pointer downward to actual number of bytes read,
plus an explicit null termination */
bytes_read += 1;
ptr = realloc(ptr, bytes_read);
ptr[bytes_read - 1] = '\0';