I'm developing very simple ftp client. I have created a data connection sockets, but I can't transfer file successfully:
FILE *f = fopen("got.png", "w");
int total = 0;
while (1){
memset(temp, 0, BUFFSIZE);
int got = recv(data, temp, sizeof(temp), 0);
fwrite(temp, 1, BUFFSIZE, f);
total += got;
if (total == 1568){
break;
}
}
fclose(f);
BUFFSIZE = 1568
I know that my file is 1568 bytes size, so I try to download it just for a test. Everything is file when I try to download .xml or .html files, but nothing good happens when I try to download png or avi files. Simply original file size is 1568 but got.png file size is 1573. I can't figure out what might cause that.
EDIT:
I have modified my code, so now it looks like (it can accept any file size):
FILE *f = fopen("got.png", "w");
while (1){
char* temp = (char*)malloc(BUFFSIZE);
int got = recv(data, temp, BUFFSIZE, 0);
fwrite(temp, 1, got, f);
if (got == 0){
break;
}
}
fclose(f);
Still received file is 2 bytes too long.
You are opening the file in text mode, so bare CR/LF characters are going to get translated to CRLF pairs when written to the file. You need to open the file in binary mode instead:
FILE *f = fopen("got.png", "wb");
You are always writing a whole buffer even if you have received only a partial one. This is the same problem with ~50% of all TCP questions.
The memset is not necessary. And I hope that temp is an array so that sizeof(temp) does not evaluate to the native pointer size. Better use BUFFSIZE there as well.
Seeing your edit, after fixing the first problem there is another one: Open the file in binary mode.
Related
I am trying to design a small file system.
I have created a text file to store the files data in.
int kufs_create_disk(char* disk_name, int disk_size){
FILE* file_ptr = fopen(disk_name, "w");
if (file_ptr == NULL)
return -1;
fseek (file_ptr, disk_size * 1024-1, SEEK_SET);
fwrite("", 1, sizeof(char), file_ptr); // to make a size for the file
fclose(file_ptr);
DiskName=disk_name;
return 0;
}
After writing to the file I get a file with the size I determine when I call the function.
kufs_create_disk("test.txt", 5);
which creates a file with size of 5kbs with '\0' to fill this file to the size.
I have created another function to write to this file in different places of the file which works just fine and I won't paste the code for simplicity.
When I try to read from the file using fread(), I'm not getting all the data I have written into the memory; rather I get just some of the data.
My read implementation would be:
int kufs_read(int fd, void* buf, int n){
FILE *file_ptr= fopen("test.txt","a+");
fseek (file_ptr, FAT[fd].position, SEEK_SET); //where FAT[fd].position is where I want to start my read and fd is for indexing purposes
fread(buf, 1, n, file_ptr); //n is the number of bytes to be read
FAT[fd].position = FAT[fd].position + n;
}
The thing is the file reads some of the characters written and doesn't read the rest. I did a little test by looping all over the file and checking whether every thing is being read and fread reads every thing but in the buf I only get some of the characters I've written.
The text file looks something like this:
0\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00written string1written string2 0\00\00\00\00\00\00\00\00\00\00\00\000\00\00\00\00\00\00\00\00\00\00\00\00writtenstring 3 \00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00
I get writtenstring1 and writtenstring2 in the buffer but I don't get writtenstring 3 for example.
Can you explain why?
I am trying to write 100 integers to binary file. I have tried writing to this file, and reading from it. When reading from it I get completely random digits.
Here is the block concerning the write.
Do note I have the file open for write with "wb" mode. I have also closed the file at the end.
for (int i = 0; i < 99; i++) {
fwrite(&i, sizeof(int), 1, file);
}
Here is the block concerning the read.
Do note I do have the file open here in "rb" mode and it is closed.
int num;
for (int i = 0; i < 100; i++) {
int rc = getc(file);
if (rc == EOF) {
fputs("Error occured while reading file", stderr);
return EXIT_FAILURE;
}
fread(&num, sizeof(int), 1, file);
printf("%d", num);
}
My output is like this:
-13421772802147469895-168955699232767012640583688388440-104919389914260634872147467638000128293273683884400-19797114882147440795-168947558432767-1097029212883066888388440148657280313254001912147440795-168942592032767-109702911303445504838844014865730434362077432147440795-168935577632767-1097029063753420883766251486573257-6039796492147440795-168932864032767-109702901326841856838844014865733541270-168949760032767-10970289133241241683884401486573450-1090518913214744079500196944831217016018891752457584192041348617175279241952408940298110176910929517683167731702125413116313304413809989891296126535181930809719192433591818324585127960891517680423011935761967-13421772802147469895-168955699232767012640583688388440-104919389914260634872147467638000128293273683884400-19797114882147440795-168947558432767
So there is something wrong, and I am not sure what exactly. Perhaps I am not sure if I understand the API for reading/writing completely (specifically size_t nitems)? I am not sure how to tell how many bytes I need to read/write from a file.
In the first loop, you are writing 100 integers starting at the address of 'i', 99 times.
Not what I think you were thinking you were doing.
it should be
fwrite(&i, sizeof(int), 1, file);
Secondly, what mode do you open the file for writing? It should be opened in binary mode otherwise it will not save binary data correctly (add 'b' to the fopen mode value)
DO you close and reopen the file for the read (and set the right file mode?) or if I was left open, do you fseek back to beginning of the file before trying to read the values.
sorry if this is a dumb question, but I'm kind of inexperienced with C. I'm trying to create a simple TCP client/server connection. This is how it works: the server stores several files, including .txt and .jpg. When the client wants one of them, he sends the name of the file to the server, reading and writing it to a certain location.
Everything works fine, except for the fact that the server's response is "RES status size data" . I'm only interested in the data to create the new file, but I'm not being able to deny the writing of RES status size into the output file. I tried to use strtok but am getting a segfault because of it, dunno why. This is the section of code where I receive the response from the server and start writing it to a new file fp.
while(success == 0)
{
while(f_block_sz = recvfrom(fd2, buffer, sizeof(buffer), 0, (struct sockaddr*)&serveraddr2, &addrlen))
{
if(f_block_sz < 0)
{
printf("Receive file error.\n");
break;
}
int write_sz = fwrite(buffer, sizeof(char), f_block_sz, fp);
if(write_sz < f_block_sz)
{
printf("File write failed.\n");
break;
}
bzero(buffer, LENGTH);
}
printf("ok!\n");
success = 1;
fclose(fp);
}
I don't even know what's best. Should I remove those "useless" words before writing to the newfile, or should I edit the file after it's finished?
Thanks in advance.
I not sure but i just thought if your "RES status size" is of fixed size, say 'x' bytes then while writing to the output file just skip x bytes of received data...
int write_sz = fwrite(buffer + x, sizeof(char), f_block_sz, fp);
I am not sure this is good practice or even correct, i just gave in my thought.
I'm trying to write PCM data (in a separate file) to the wav file I'm creating. I have already written the header and confirmed that worked but for some reason when I try to write the raw data into it, it doesn't. What I have successfully read the pcm file to a buffer and got the size of the file. The fwrite() process didn't give me an error either during compile however the resulting file is still empty. Any help is much appreciated! Thanks!
register FILE *handle;
register FILE *lever;
char filename[] = "test.wav";
handle = fopen(filename, "w");
lever = fopen("test.pcm","rb");
fseek(lever, 0, SEEK_END);
long int lSize = ftell(lever);
printf("%i \n",lSize);
rewind(lever);
char *buffer = (char*) malloc(sizeof(char)*lSize);
if (NULL == buffer) {printf("Error creating buffer \n");}
if (lSize != fread(buffer, 1, lSize, lever)) {
printf("Reading error \n");
}
fwrite(buffer, sizeof(buffer), 1, handle);
free(buffer);
fclose(lever);
fclose(handle);
Change
fwrite(buffer, sizeof(buffer), 1, handle);
into:
fwrite(buffer, lSize, 1, handle);
If you are running this under windows, it could fail because of text mode output file.
Use handle = fopen(filename, "wb");. Are you sure you have no errors while opening the files? Also your way of getting file size isn't optimal and it's better to use stat-family functions. And if malloc will fail you'll get a "Segmentation fault".
fwrite returns number of items written, or -1 on error. It sets errno so you can use perror or strerror.
EDIT: wildplasser's answer is the correct solution. Didn't notice this mistake.
I have written a program which takes a file as input and whenever it finds a line with length > 80, it adds \ and \n to that file to make it 80 chars in width max.
The problem is that I have used fseek to insert \ and \n whenever the length exceeds 80, so it overrides two characters of that line which exceeds length 80. Is there a way using which I can insert text without overriding the existing text?
Here is my code:-
#include<stdio.h>
#include<string.h>
int main(int argc, char *argv[])
{
FILE *fp1,*fp2;
int prev=0,now=0;
char ch;
int flag=0;
long cur;
fp1=fopen(argv[1],"r+");
if(fp1==NULL){
printf("Unable to open the file to read. Program will exit.");
exit(0);
}
else{
while((ch=fgetc(fp1))!=EOF){
if(ch!=' ' && ch!='\n'){
now=now+1;
}
else{
if(now>=80){
fseek(fp1,cur,SEEK_SET);
fputc('\\',fp1);
fputc('\n',fp1);
now=0;
continue;
}
if(ch=='\n'){
flag=0;
now=0;
continue;
}
else{
prev=now;
cur=ftell(fp1);
}
now=now+1;
}
}
}
fclose(fp1);
return 0;
}
To run it, you need to do following:-
user#ubuntu$ cc xyz.c
user#ubuntu$ ./a.out file_to_check.txt
While there are a couple of techniques to do it in-place, you're working with a text file and want to perform insertions. Operating systems typically don't support text file insertions as a file system primitive and there's no reason they should do that.
The best way to do that kind of thing is to open your file for reading, open a new file for writing, copy the part of the file before the insertion point, insert the data, copy the rest, and then move the new file over the old one.
This is a common technique and it has a purpose. If anything goes wrong (e.g. with your system), you still have the original file and can repeat the transaction later. If you start two instances of the process and use a specific pattern, the second instance is able to detect that the transaction has already been started. With exclusive file access, it can even detect whether the transaction was interrupted or is still running.
That way is much less error prone than any of the techniques performed directly on the original file and is used by all of those traditional tools like sed even if you ask them to work in-place (sed -i). Another bonus is that you can always rename the original file to one with a backup suffix before overwriting it (sed offers such an option as well).
The same technique is often used for configuration files even if your program is writing an entirely new version and doesn't use the original file for that. It hasn't been long since many internet magazines claimed that ext4 accidentally truncates configuration files to zero length. This was exactly because some applications kept the configuration files open and truncated while the system was forcedly shut down. Those application often tampered with the original configuration files before they had the data ready and then even kept them open without syncing them, which made the window for data corruption much larger.
TL;DR version:
When you value your data, don't destroy it before you have the replacement data ready.
No, there's no way to insert characters into an existing file. You will need to use a second file to do that.
This is the function I use for this kind of thing:
int finsert (FILE* file, const char *buffer) {
long int insert_pos = ftell(file);
if (insert_pos < 0) return insert_pos;
// Grow from the bottom
int seek_ret = fseek(file, 0, SEEK_END);
if (seek_ret) return seek_ret;
long int total_left_to_move = ftell(file);
if (total_left_to_move < 0) return total_left_to_move;
char move_buffer[1024];
long int ammount_to_grow = strlen(buffer);
if (ammount_to_grow >= sizeof(move_buffer)) return -1;
total_left_to_move -= insert_pos;
for(;;) {
u16 ammount_to_move = sizeof(move_buffer);
if (total_left_to_move < ammount_to_move) ammount_to_move = total_left_to_move;
long int read_pos = insert_pos + total_left_to_move - ammount_to_move;
seek_ret = fseek(file, read_pos, SEEK_SET);
if (seek_ret) return seek_ret;
fread(move_buffer, ammount_to_move, 1, file);
if (ferror(file)) return ferror(file);
seek_ret = fseek(file, read_pos + ammount_to_grow, SEEK_SET);
if (seek_ret) return seek_ret;
fwrite(move_buffer, ammount_to_move, 1, file);
if (ferror(file)) return ferror(file);
total_left_to_move -= ammount_to_move;
if (!total_left_to_move) break;
}
seek_ret = fseek(file, insert_pos, SEEK_SET);
if (seek_ret) return seek_ret;
fwrite(buffer, ammount_to_grow, 1, file);
if (ferror(file)) return ferror(file);
return 0;
}
Use it like this:
FILE * file= fopen("test.data", "r+");
ASSERT(file);
const char *to_insert = "INSERT";
fseek(file, 3, SEEK_SET);
finsert(file, to_insert);
ASSERT(ferror(file) == 0);
fclose(file);
This (as others here have mentioned) can theoretically corrupt a file if there is an error, but here is some code to actually do it... Doing it in-place like this is usually fine, but you should backup the file if you are worried about it...
No, there is no way. You have to create a new file or move the contents of the file 2 characters backwards.
You can load the file as chunks (in your case is 80 characters) and then append two character (new line) and write the content into anohter file.
another implementation use tmpfile()
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
FILE *tmp_buf;
int finsert(FILE *f, const char* msg){
fseek(tmp_buf, 0, SEEK_SET);
fpos_t f_pos;
assert (fgetpos(f, &f_pos)==0);
char buf[50];
while(fgets(buf, 50, f))
fputs(buf, tmp_buf);
long tmp_buf_pos = ftell(tmp_buf);
fsetpos(f, &f_pos);
fputs(msg, f);
fseek(tmp_buf, 0, SEEK_SET);
while(--tmp_buf_pos>=0)
fputc(fgetc(tmp_buf), f);
return ferror(f);
}
int main()
{
FILE *f = fopen("result.txt", "wb+");
assert (f!=NULL);
fputs("some text", f);
tmp_buf = tmpfile();
assert (tmp_buf!=NULL);
assert(finsert(f, "another text")==0);
fclose (f);
perror("");
}
tested in Cygwin64