If I write
if((fp = fopen(some_path, "wb")))
{
int a = 50000;
fwrite("c",sizeof(char),a,fp);
fclose(fp);
fp = fopen(some_path, "rb");
char arr[50000];
fread(arr, sizeof(char), a, fp);
printf("%s\n", arr);
fclose(fp);
}
it prints "c" of course but the file is ~50kb
My questions are:
How is this actually working?
If I modify var a to 60000 the executable crashes, so i`m thinking about some internal buffer of fwrite. How do i get its max capacity?
What does fwrite() write to the file in order to get the file to ~50kb of size and still print only "c"(I was expecting some mambo-jumbo characters here)?
How wrong is this usage of the function, I want to write a blank file of a certain size really fast(with dummy data), would I be wrong exploiting this behaviour in order not to make a big buffer and use up memory to write "real" data but still reduce fwrite calls(I may need to write a 10 gb file for ex.)?
How is this actually working?
I would argue that it isn't working. You did something nonsense, and it went uncaught. The gave you the impression that it will work in the future. That's a failure.
If I modify var a to 60000 the executable crashes, so i`m thinking about some internal buffer of fwrite. How do i get its max capacity?
There's no buffer. You are merely accessing whatever is in memory after the c␀ created by "c". When it crashes, it's because you've reached a memory page that can't be read (e.g. hasn't been allocated).
What does fwrite() write to the file in order to get the file to ~50kb of size
Whatever happens to be in memory at the address returned by "c" and beyond.
and still print only "c"(I was expecting some mambo-jumbo characters here)?
It doesn't print only c. Try something like hexdump -C file or od -c file
How wrong is this usage of the function
Incredibly. It could crash for any value of a larger than 2.
I want to write a blank file of a certain size really fast(with dummy data)
The docs for truncate says: "If the file previously was shorter, it is extended, and the extended part reads as null bytes ('\0')." So you could use the following:
if (truncate(path, length)) {
perror("truncate");
exit(1);
}
Related
I have been trying for a long time to figure out how to get the program to read text from a file. I have tried a solution with fgets() and a loop. The program runs but does not print the variable, indicating the text was not extracted.
#include <stdio.h>
#include <string.h>
#include "stdfn.h"
int main(int argc, char* argv[])
{
char* request;
char bf_text[1024];
char character;
intro("Site Blocker", 2014, "MIT");
request = bash("curl http://redsec.ru/blocked_sites.txt"); // Source of bash() is at line 139 https://github.com/Pavelovich/lib-c/blob/master/stdfn.h
//printf("%s", request);
FILE* block_file = fopen(".blocked_sites", "w+"); // Changed from "w" based on this thread, currently only outputs a small part of the file.
FILE* hosts = fopen("hosts", "w");
FILE* hosts_tmp = fopen(".hosts", "w");
// Print the text of the web request to the temporary
// .blocked_sites file
fprintf(block_file, "%s", request);
rewind(block_file);
fread(bf_text, sizeof(block_file), 1, block_file);
printf("%s", bf_text);
fclose(block_file);
return 0;
}
sizeof(block_file) does not give you the size of the file. It'll give you the size of a file pointer, probably either four or eight bytes. Probably eight in your case, since you're saying it's reading "74.125.2", which is eight bytes, and then going haywire. You'll need to use something like stat() on a POSIX system, or a combination of fseek() and ftell().
You should also open files in binary mode if you're going to use fread() or fwrite(), since they are binary file IO functions. It won't make a difference on UNIX systems, but it may well on Windows, for instance. You shouldn't really mix text and binary mode IO functions in the way that you have for this reason.
You should also be checking the returns from your fopen() calls to make sure they succeeded.
And that bash() function you're using is completely broken, too. You'll get a memory leak every time it's called because it never free()s output, and it's making the same sizeof error that you are, although it'll still work because of the loop it's in. It'll just waste all that memory it allocated. And you are leaking memory because you never free(request). And you'd better never #include it in more than one translation unit, either, unless you want multiple definition errors all over the place. That whole "library" is riddled with schoolboy-type errors, in fact, including repeated failures to check the return from malloc(), allocating memory to fit a pointer instead of the thing it's pointing at, and so on.
You are opening block_file for "write only". Try changing the mode parameter to "w+" i.e.
FILE block_file = fopen(".blocked_sites", "w+");
If you want to open an existing file rather than creating a new one each time, use "r+" or "a+" instead of "w+".
I have no experience with fscanf() and very little with functions for FILE. I have code that correctly determines if a client requested an existing file (using stat() and it also ensures it is not a directory). I will omit this part because it is working fine.
My goal is to send a string back to the client with a HTTP header (a string) and the correctly read data, which I would imagine has to become a string at some point to be concatenated with the header for sending back. I know that + is not valid C, but for simplicity I would like to send this: headerString+dataString.
The code below does seem to work for text files but not images. I was hoping that reading each character individually would solve the problem but it does not. When I point a browser (Firefox) at my server looking for an image it tells me "The image (the name of the image) cannot be displayed because it contains errors.".
This is the code that is supposed to read a file into httpData:
int i = 0;
FILE* file;
file = fopen(fullPath, "r");
if (file == NULL) errorMessageExit("Failed to open file");
while(!feof(file)) {
fscanf(file, "%c", &httpData[i]);
i++;
}
fclose(file);
printf("httpData = %s\n", httpData);
Edit: This is what I send:
char* httpResponse = malloc((strlen(httpHeader)+strlen(httpData)+1)*sizeof(char));
strcpy(httpResponse, httpHeader);
strcat(httpResponse, httpData);
printf("HTTP response = %s\n", httpResponse);
The data part produces ???? for the image but correct html for an html file.
Images contain binary data. Any of the 256 distinct 8-bit patterns may appear in the image including, in particular, the null byte, 0x00 or '\0'. On some systems (notably Windows), you need to distinguish between text files and binary files, using the letter b in the standard I/O fopen() call (works fine on Unix as well as Windows). Given that binary data can contain null bytes, you can't use strcpy() et al to copy chunks of data around since the str*() functions stop copying at the first null byte. Therefore, you have to use the mem*() functions which take a start position and a length, or an equivalent.
Applied to your code, printing the binary httpData with %s won't work properly; the %s will stop at the first null byte. Since you have used stat() to verify the existence of the file, you also have a size for the file. Assuming you don't have to deal with dynamically changing files, that means you can allocate httpData to be the correct size. You can also pass the size to the reading code. This also means that the reading code can use fread() and the writing code can use fwrite(), saving on character-by-character I/O.
Thus, we might have a function:
int readHTTPData(const char *filename, size_t size, char *httpData)
{
FILE *fp = fopen(filename, "rb");
size_t n;
if (fp == 0)
return E_FILEOPEN;
n = fread(httpData, size, 1, fp);
fclose(fp);
if (n != 1)
return E_SHORTREAD;
fputs("httpData = ", stdout);
fwrite(httpData, size, 1, stdout);
putchar('\n');
return 0;
}
The function returns 0 on success, and some predefined (negative?) error numbers on failure. Since memory allocation is done before the routine is called, it is pretty simple:
Open the file; report error if that fails.
Read the file in a single operation.
Close the file.
Report error if the read did not get all the data that was expected.
Report on the data that was read (debugging only — and printing binary data to standard output raw is not the best idea in the world, but it parallels what the code in the question does).
Report on success.
In the original code, there is a loop:
int i = 0;
...
while(!feof(file)) {
fscanf(file, "%c", &httpData[i]);
i++;
}
This loop has a lot of problems:
You should not use feof() to test whether there is more data to read. It reports whether an EOF indication has been given, not whether it will be given.
Consequently, when the last character has been read, the feof() reports 'false', but the fscanf() tries to read the next (non-existent) character, adds it to the buffer (probably as a letter such as ÿ, y-umlaut, 0xFF, U+00FF, LATIN SMALL LETTER Y WITH DIAERESIS).
The code makes no check on how many characters have been read, so it has no protection against buffer overflow.
Using fscanf() to read a single character is a lot of overhead compared to getc().
Here's a more nearly correct version of the code, assuming that size is the number of bytes allocated to httpData.
int i = 0;
int c;
while ((c = getc(file)) != EOF && i < size)
httpData[i++] = c;
You could check that you get EOF when you expect it. Note that the fread() code does the size checking inside the fread() function. Also, the way I wrote the arguments, it is an all-or-nothing proposition — either all size bytes are read or everything is treated as missing. If you want byte counts and are willing to tolerate or handle short reads, you can reverse the order of the size arguments. You could also check the return from fwrite() if you wanted to be sure it was all written, but people tend to be less careful about checking that output succeeded. (It is almost always crucial to check that you got the input you expected, though — don't skimp on input checking.)
At some point, for plain text data, you need to think about CRLF vs NL line endings. Text files handle that automatically; binary files do not. If the data to be transferred is image/png or something similar, you probably don't need to worry about this. If you're on Unix and dealing with text/plain, you may have to worry about CRLF line endings (but I'm not an expert on this — I've not done low-level HTTP stuff recently (not in this millennium), so the rules may have changed).
I am looking for the faster way to completely change the content of a file. It will be clear after the example:
a.txt:
I am a very very long (maybe not too long) file. I am pretty sure I could be longer.
After running the program, and according to the user's input it should become for instance:
user input:
Hi!
Then I tried to use fwrite.
The problem is that the rest of the file were still there, so I've got something like:
a.txt:
Hi!m a very very long (maybe not too long) file. I am pretty sure I could be longer
After some researching this is what I've done:
FILE
*a;
char
buffer[500];
a = fopen("a.txt", "r");
fread(buffer, sizeof(char), 500, a);
printf("%s\n", buffer);
a = freopen("a.txt", "w", a);
scanf("%s", buffer);
// rewind(a);
// fwrite(buffer, sizeof(char), strlen(buffer), a);
fwrite(buffer, sizeof(char), 10, a);
fclose(a);
Although it works, I want to know if there's a better way to do it.
You can just use ftruncate() POSIX function after fwrite() to truncate the file.
The "w" flag should create a file, truncating any existing content if it exists. That's what you need.
I haven't used freopen() before but suspect that's related to the problem. I would try simply closing the input file with fclose(), and then open the file again using fopen()?
I don't know of a better way to do this using portable C.
There are a few minor problems with your implementation.
You don't check for any errors that might have occurred, which is important in the real world. Especially freopen might return NULL on error, and if you assign that to you're original pointer you lose the ability to fclose the file.
You should also remember that normal C strings end with a 0 byte, but fread reads raw bytes so you should reserve space for that zero byte and provide it. scanf will write the zero byte so you can use strlen to determine how many bytes to tell fwrite to write instead of hardcoding 10.
Finally scanf is easy for mocking stuff up, but the way you have it now if the user provides more than 499 bytes you'll have a buffer overflow which can lead to very bad things.
I have, as usual, been reading quite a few posts on here. I found a particular useful posts on bus errors in general, see here. My problem is that I cannot understand why my particular code is giving me an error.
My code is an attempt to teach myself C. It's a modification of a game I made when I learned Java. The goal in my game is to take a huge 5049 x 1 text file of words. Randomly pick a word, jumble it and try to guess it. I know how to do all of that. So anyway, each line of the text file contains a word like:
5049
must
lean
better
program
now
...
So, I created an string array in C, tried to read this string array and put it into C. I didn't do anything else. Once I get the file into C, the rest should be easy. Weirder yet is that it complies. My problem comes when I run it with ./blah command.
The error I get is simple. It says:
zsh: bus error ./blah
My code is below. I suspect it might have to do with memory or overflowing the buffer, but that's completely unscientific and a gut feeling. So my question is simple, why is this C code giving me this bus error msg?
#include<stdio.h>
#include<stdlib.h>
//Preprocessed Functions
void jumblegame();
void readFile(char* [], int);
int main(int argc, char* argv[])
{
jumblegame();
}
void jumblegame()
{
//Load File
int x = 5049; //Rows
int y = 256; //Colums
char* words[x];
readFile(words,x);
//Define score variables
int totalScore = 0;
int currentScore = 0;
//Repeatedly pick a random work, randomly jumble it, and let the user guess what it is
}
void readFile(char* array[5049], int x)
{
char line[256]; //This is to to grab each string in the file and put it in a line.
FILE *file;
file = fopen("words.txt","r");
//Check to make sure file can open
if(file == NULL)
{
printf("Error: File does not open.");
exit(1);
}
//Otherwise, read file into array
else
{
while(!feof(file))//The file will loop until end of file
{
if((fgets(line,256,file))!= NULL)//If the line isn't empty
{
array[x] = fgets(line,256,file);//store string in line x of array
x++; //Increment to the next line
}
}
}
}
This line has a few problems:
array[x] = fgets(line,256,file);//store string in line x of array
You've already read the line in the condition of the immediately preceding if statement: the current line that you want to operate on is already in the buffer and now you use fgets to get the next line.
You're trying to assign to the same array slot each time: instead you'll want to keep a separate variable for the array index that increments each time through the loop.
Finally, you're trying to copy the strings using =. This will only copy references, it won't make a new copy of the string. So each element of the array will point to the same buffer: line, which will go out of scope and become invalid when your function exits. To populate your array with the strings, you need to make a copy of each one for the array: allocate space for each new string using malloc, then use strncpy to copy each line into your new string. Alternately, if you can use strdup, it will take care of allocating the space for you.
But I suspect that this is the cause of your bus error: you're passing in the array size as x, and in your loop, you're assigning to array[x]. The problem with this is that array[x] doesn't belong to the array, the array only has useable indices of 0 to (x - 1).
You are passing the value 5049 for x. The first time that the line
array[x] = ...
executes, it's accessing an array location that does not exist.
It looks like you are learning C. Great! A skill you need to master early is basic debugger use. In this case, if you compile your program with
gcc -g myprogram.c -o myprogram
and then run it with
gdb ./myprogram
(I am assuming Linux), you will get a stack dump that shows the line where bus error occurred. This should be enough to help you figure out the error yourself, which in the long run is much better than asking others.
There are many other ways a debugger is useful, but this is high on the list. It gives you a window into your running program.
You are storing the lines in the line buffer, which is defined inside the readFile function, and storing pointers to it in the arary. There are two problems with that: you are overwriting the value everytime a new string is read and the buffer is in the stack, and is invalid once the function returns.
You have at least a few problems:
array[x] = fgets(line,256,file)
This stores the address of line into each array element. line in no longer valid when readFile() returns, so you'll have an array of of useless pointers. Even if line had a longer lifetime, it wouldn't be useful to have all your array elements having the same pointer (they'd each just point to whatever happened to be written in the buffer last)
while(!feof(file))
This is an antipattern for reading a file. See http://c-faq.com/stdio/feof.html and "Using feof() incorrectly". This antipattern is likely responsible for your program looping more than you might expect when reading the file.
you allocate the array to hold 5049 pointers, but you simply read however much is in the file - there's no checking for whether or not you read the expected number or to prevent reading too many. You should think about allocating the array dynamically as you read the file or have a mechanism to ensure you read the right amount of data (not too little and not too much) and handle the error when it's not right.
I suspect the problem is with (fgets(line,256,file))!=NULL). A better way to read a file is with fread() (see http://www.cplusplus.com/reference/clibrary/cstdio/fread/). Specify the FILE* (a file stream in C), the size of the buffer, and the buffer. The routine returns the number of bytes read. If the return value is zero, then the EOF has been reached.
char buff [256];
fread (file, sizeof(char), 256, buff);
Do I need to malloc when creating a file to write to?
The file will be based on the contents of 2 others, so would I need to malloc space for the writeable file of sizeof( file a ) + sizeof( file b) + 1?
Sorry if this makes no sense; if it doesn't then I guess I need to go read some more :D
Essentially, I have 2 txt files and a string sequence - I am writing each line of each file side by side separated by the string sequence.
txt file a
hello stack over
flow this
is a test
txt file b
jump the
gun i am
a novice
seperator == xx
output ==
hello stack overxxjump the
flow thisxxgun i am
is a testxxa novice
If you're writing it in order, can't you just use fprintf() or fwrite() whenever you need to write something out, instead of writing the entire file at once?
EDIT: Based on your update, here's basically what you have to do (probably not valid C since I'm not a C programmer):
EDIT2: With some help from msw:
const int BUFSIZE = 200;
FILE *firstFile = fopen("file1.txt", "r");
FILE *secondFile = fopen("file2.txt", "r");
FILE *outputFile = fopen("output.txt", "w");
char* seperator = "xx";
char firstLine[BUFSIZE], secondLine[BUFSIZE];
// start a loop here
fgets(firstLine, 200, firstFile);
fgets(secondLine, 200, secondFile);
// Remove '\n's from each line
fprintf(outputFile, "%s%s%s", firstLine, seperator, secondLine);
// end a loop here
fclose(outputFile);
fclose(firstFile);
fclose(secondFile);
You only need to malloc the entire size of a file if you need to hold the entire file in memory (and even then, you can probably use mmap or something instead). Allocate as much memory as you need for the data you intend to work with in memory: no more, no less.
Files are on disk, malloc is for RAM.
You'd only malloc if you needed space in memory to store the data PRIOR to writing out to the file, otherwise, typically you'd use a stack allocated buffer of say 8k to write to the file in chunks.
So taking your question as-is, no you'd rarely malloc just to write to a file.
If your goal is to keep the file in memory in completion, then you'd malloc sizeof file.