Alternatives of fopen, fread for reading from string? - c

I have a code which integrates with a certain library I cannot modify and reads from file. I need to turn it into reading from string instead of from file.
string str = "I now have the string from file.txt in memory";
// original code:
FILE *file = fopen("file.txt", "rb");
// ...
uint8_t buffer[128];
// ...
var->var1 = buffer;
// ...
var->var2 = fread(buffer, 1, 128, file);
// ...
So a simple question, what's an alternative for fread for reading from string into buffer?
Platform: Windows

If you don't require a FILE *, if you just need to copy counted characters from one buffer (or string) to another, that's what memcpy is for. For example, to a first approximation, you could replace
fread(buffer, 1, 128, file);
with
memcpy(buffer, str, 128);
Now, this will break pretty badly if str does not contain 128 characters. (If your file had less than 128 characters on it, fread would give you less than 128.) So a safer replacement would be
int n = 128;
if(strlen(str) < n) n = strlen(str);
memcpy(buffer, str, n);

There is a function that does precisely what you want: fmemopen. You hand it a pointer to an in-memory string buffer, and a "r" or "w" flag (just like fopen), and it gives you a regular old FILE * that you can read from or write to -- or, in your case, pass to a function that needs a FILE * to read from or write to.
It's available in glibc and therefore in virtually all versions of Linux. I think it's available in some versions of Unix. I don't seem to have it on my Mac. I'm afraid you'll probably have a hard time finding it for Windows.

If i understand your task correctly, you need to get information out of a string as if they would been written from a file. A very comfortable way to solve that is std::stringstream, if you are using C++.

Related

Read txt file from standard input and store whole text into char array

Let me describe what I am trying to do.
Compile the source and run with an input text file
gcc main.c -o main
./main < text.txt
Then the text inside 'text.txt' should be stored into one single variable. I am not sure, but I think this should be as char array.
Ultimately, I want loop through each character of 'text.txt' with its index; in python it should be like as follows
i = 0
while i < len(string):
print(string[i])
if (~~~):
i -= 10(or some other numbers)
else:
i += 1
Several points to consider
i can be decremented and print again from the i-10th character.
The program doesn't know the length of the input text file.
I cannot specify file directory in the source; the input text must come through standard input in the terminal.
How can I write this in C?
The following was my try in C, didn't work though.
int main(){
char A;
short i;
scanf("%s", A);
printf("%s", A);
for (i=0; i < strlen(A); i++) {
printf("%c", A[i]);
}
return 0;
}
You can do it multiple ways, lets start by getting proper length of file.
FILE *f = fopen("textfile.txt", "rb");
fseek(f, 0, SEEK_END); // goes to the end of file
long fsize = ftell(f); // tells what position in bytes is
fclose (f)
f = fopen("textfile.txt", "r");
You noticed that I am using rb or read binary. thats because to get over the fact that if you use fseek() and ftell() in non binary mode you are getting into undefined behavior and results may differ between Windows machine and (*)nix machine.
Now how to read a text file into the string. There are multiple ways
using getc() not preferred
using fscanf() not preferred either, better than getc() in a way that it reads mutiple bytes at the time
using fgets() you can specify the size of the file you read thus avoiding buffer overflow.
Above is quick and dirty way of reading, without knowing what is your DE like (what OS, compiler etc) I could go into more details how to do it, but tnx to #Nail here is an article, you can read. You could use it and have flags for different OSs instead of using my method.

How is the best way to completely change a file content in C?

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.

replace bytes in file c

I got some code and I want improve it to find and replace bytes in file
so
I want to find all origbytes in FILE and then replace it with newbytes
then save file, I know how to open, write and save, but hot I can find bytes in char?
FILE* file;
file = fopen("/Users/Awesome/Desktop/test", "r+b");
int size = sizeof(file)+1;
char bytes [size];
fgets(bytes, size, file);
for (int i=0; i<size; i++){
char origbytes [] = {0x00, 0x00};
char newbytes [] = {0x11, 0x11};
if (strcmp(bytes[i], origbytes)) //Here the problem
{
fseek(file, i, SEEK_SET);
fwrite(newbytes, sizeof(newbytes), 1, file);
}
}
fclose(file);
strcmp() is for string compare and not character compare. Two characters can be compared directly
if ( bytes[i] == origbytes[something] )
Also you you should not apply sizeof() on a file pointer to determine file size. You should seek to the end of file using fseek and then query ftell except for binary files. For binary files, use something like fstat
One more thing to note is that fgets returns much before the EOF if it sees a newline. Hence in your code, you may not read entire file contents even after doing the changes that we suggested. You need to use fread appropriately
Strings are null terminated in the C standard library. Your search data is effectively a zero length string. You want memcmp instead.
memcmp (&bytes [i], origBytes, 2)
Firstly sizeof(file) + 1 just returns you the size of a pointer + 1. I don't think you need this for the size of the file. Use this: How do you determine the size of a file in C?
Then since you compare bytes (more or less smae as char) you simply compare using =
you can use fseek and then ftell functions to get the file size, not sizeof.

How to send() an image by saving it in char string in ANSI C? Problem with casting

I'm writing my own server in ANSI C (on Linux) and I've got one problem with sending images to a web browser. I'm using the function "send()" to send it and it required a char *. So I was reading a file char by char (using fgetc), I used fread and fgets, but everything had a problem with casting the content(int in most cases) to a bufor - some bytes still were missing (e.g 2008 was send instead of 2020). I think there is some problem with conversion, but I used sprintf and wchar_t and it is still not working.
I've got a function:
void send_www(char *buff, int cd){
if(send (cd, buff, strlen(buff), 0) < 0) {
perror ("send()");
exit (1);
which I use here:
// while (fread(znak, sizeof(char), i, handler) != 0) {
for (j = 0; j < i; j++) {
a = fgetc(handler);
sprintf(znak, "%ls", a);
send_www(znak, cd);
}
where i is the length of the image file, znak is the char* (in this version wchar_t*). You can also see my earlier try of using fread.
It's hard to say without more details - can you post some code?
One thing is to make sure you open the image file with the binary option, e.g. fopen(filename, "rb");
If you're just reading a binary file and sending it to a socket where the other end is expecting binary you should not need any casting or sprintf.
You seem to be confusing char type with strings. A C string is a sequence of characters that is zero terminated. Your image file is a binary file containing a sequence of characters but it is not a string. The send() function takes a pointer to a sequence of bytes, not strings. To illustrate this look at this sequence of chars (or bytes):
255, 255, 255, 0, 0, 0, 255, 255, 255
In some image file formats this can be three RGB pixels, so white, black white. 9 bytes. Can be held in a char buffer[9] . If you call strlen() on this you will get the result 3. strlen() counts until it sees a zero character. If you read these bytes from a file without the binary flag it may get transformed and you may get less or more bytes than are really in the file (depending on the contents, the OS etc.).
Rough explanatory code follows:
char buffer[1024];
FILE *infile = fopen("test.gif", "rb"); // open a file (check for failure in real code)
int nread = fread(buffer, 1, 1024, infile);
// assume socket is connected (check for number of bytes sent or error in real code)
send(socket, buffer, nread, 0);
fclose(infile); // close the file
To read from a binary file and send the data out to a socket you need to do something like the snippet above. You would probably keep reading until you reach the end of file, the snippet only reads once. Also in real code you should check to see how many bytes were actually sent (in the return value from send) and keep calling send until you've sent all the data or an error has occured.
You are using strlen(). strlen() stops and returns the size after it reaches a \0 byte. Images and binary data are allowed to have a byte valued zero anywhere, so strlen() is useless in those cases.
You need to modify your function to:
send_www(unsigned char *buff, size_t buf_len int cd);
And pass the image size to it, so you can safely send() it with the appropriate size.
In your case, it seems you are sure it is a wchar_t string, use the appropriate function, wcslen(), not strlen() :)
I'd guess that the problem is that your image data contain characters (\0), so they aren't being sent because you're using strlen() to figure out how much data to send.

Unexpected output copying file in C

In another question, the accepted answer shows a method for reading the contents of a file into memory.
I have been trying to use this method to read in the content of a text file and then copy it to a new file. When I write the contents of the buffer to the new file, however, there is always some extra garbage at the end of the file. Here is an example of my code:
inputFile = fopen("D:\\input.txt", "r");
outputFile = fopen("D:\\output.txt", "w");
if(inputFile)
{
//Get size of inputFile
fseek(inputFile, 0, SEEK_END);
inputFileLength = ftell(inputFile);
fseek(inputFile, 0, SEEK_SET);
//Allocate memory for inputBuffer
inputBuffer = malloc(inputFileLength);
if(inputBuffer)
{
fread (inputBuffer, 1, inputFileLength, inputFile);
}
fclose(inputFile);
if(inputBuffer)
{
fprintf(outputFile, "%s", inputBuffer);
}
//Cleanup
free(inputBuffer);
fclose(outputFile);
}
The output file always contains an exact copy of the input file, but then has the text "MPUTERNAM2" appended to the end. Can anyone shed some light as to why this might be happening?
You may be happier with
int numBytesRead = 0;
if(inputBuffer)
{
numBytesRead = fread (inputBuffer, 1, inputFileLength, inputFile);
}
fclose(inputFile);
if(inputBuffer)
{
fwrite( inputBuffer, 1, numBytesRead, outputFile );
}
It doesn't need a null-terminated string (and therefore will work properly on binary data containing zeroes)
Because you are writing the buffer as if it were a string. Strings end with a NULL, the file you read does not.
You could NULL terminate your string, but a better solution is to use fwrite() instead of fprintf(). This would also let you copy files that contain NULL characters.
Unless you know the input file will always be small, you might consider reading/writing in a loop so that you can copy files larger than memory.
You haven't allocated enough space for the terminating null character in your buffer (and you also forget to actually set it), so your fprintf is effectively overreading into some other memory. Your buffer is exactly the same size as the file, and is filled with its content, however, fprintf reads the parameter looking for the terminating null, which isn't there, until a couple of characters later where, coincidently, there is one.
EDIT
You're actually mixing two types of io, fread (which is paired with fwrite) and fprintf (which is paired with fscanf). You should probably be doing fwrite with the number of bytes to write; or conversely, use fscanf, which would null-terminate your string (although, this wouldn't allow nulls in your string).
Allocating memory to fit the file is actually quite a bad way of doing it, especially the way it's done here. If the malloc() fails, no data is written to the output file (and it fails silently). In other words, you can't copy files greater than a few gigabytes on a 32-bit platform due to the address space limitations.
It's actually far better to use a smaller memory chunk (allocated or on the stack) and read/write the file in chunks. The reads and writes will be buffered anyway and, as long as you make the chunks relatively large, the overhead of function calls to the C runtime libraries is minimal.
You should always copy files in binary mode as well, it's faster since there's no chance of translation.
Something like:
FILE *fin = fopen ("infile","rb"); // make sure you check these for NULL return
FILE *fout = fopen ("outfile","wb");
char buff[1000000]; // or malloc/check-null if you don't have much stack space.
while ((count = fread (buff, 1, sizeof(buff), fin)) > 0) {
// Check count == -1 and errno here.
fwrite (buff, 1, count, fout); // and check return value.
}
fclose (fout);
fclose (fin);
This is from memory but provides the general idea of how to do it. And you should always have copiuos error checking.
fprintf expects inputBuffer to be null-terminated, which it isn't. So it's reading past the end of inputBuffer and printing whatever's there (into your new file) until it finds a null character.
In this case you could malloc an extra byte and put a null as the last character in inputBuffer.
In addition to what other's have said: You should also open your files in binary-mode - otherwise, you might get unexpected results on Windows (or other non-POSIX systems).
You can use
fwrite (inputBuffer , 1 , inputFileLength , outputFile );
instead of fprintf, to avoid the zero-terminated string problem. It also "matches better" with fread :)
Try using fgets instead, it will add the null for you at the end of the string. Also as was said above you need one more space for the null terminator.
ie
The string "Davy" is represented as the array that contains D,a,v,y,\0 (without the commas). Basically your array needs to be at least sizeofstring + 1 to hold the null terminator. Also fread will not automatically add the terminator, which is why even if your file is way shorter than the maximum length you get garbage..
Note an alternative method for being lazy is just to use calloc which sets the string to 0. But still you should only fread inputFileLength-1 characters at most.

Resources