I'm working on a project using UDP protocol to transfer a file, but when I use strcpy() to copy a buffer into another string, it always missing some characters.
The simple idea is that:
I defined a struct:
struct frame{
int kind;//transmission(0) or retransmission(1)
int seq;
int ack;
char info[256];
};
Then I use fread to get the content of a text file into the buffer:
char buffer[256] = {0};
fread(buffer, 256, 1, fp);//read file: 256 byte
struct frame currFrame;
currFrame.ack = 0;
bzero(currFrame.info, 256);
strcpy(currFrame.info, buffer); //store the content to transfer
printf("%s\n", buffer);
printf("%s\n", currFrame.info);
The code above is in a for loop because i read file multiple times.
when I use printf(), half time, the result is right. But half time, they are different(like missing some characters in the head). How can I fix this?
The output is attached(The above is buffer, which is right):
The strcpy function is only for strings. To copy arbitrary data, use memcpy. Also, the %s format specifier is only for strings. Functions like fread read arbitrary binary data and don't try to form strings.
Also, you called fread in such a way that it won't tell you how many bytes it actually read. Unless you're positive you're always going to read exactly 256 bytes, that isn't smart. Instead, set the second parameter of fread to 1 and use the third parameter to set the maximum number of bytes to read. And don't ignore the return value -- that's how you know how many bytes it was actually able to read.
Related
I am using write to create a csv file with the following type of values on every line
int16_t, int16_t, int16_t, int64_t, uint64_t
First a buffer is filled using sprintf and the it is passed to write. However, there is only one line with all the values in the written file. No new line.
static char line[34];
sprintf(line, "%d,%d,%d,%ld,%lu\n", ...);
write(fd_csv_data, line, sizeof(line));
%d,%d,%d,%ld,%lu makes 32 bytes in total, adding \n and \0 results in 34. What am I doing wrong ?
Two problems:
You write the full buffer, even the parts that are after the null-terminator. This part could be uninitialized and have indeterminate values.
Even if you fill the buffer completely, you write the null-terminator, which shouldn't be written to a text file.
To solve both these issues, use strlen instead to get the actual length of the string:
write(fd_csv_data, line, strlen(line));
On another couple of notes:
Use snprintf instead of sprintf, to avoid possible buffer overruns
The size-prefix l might be wrong for 64-bit types, use the standard format macro constants, like PRId64 for int64_t.
Your buffer could overflow, so you'll have to calculate the maximum size of the generated string or just use a buffer big enough.
To write to the file, you can use the return value of sprintf():
static char line[256];
int n = sprintf(line, "%d,%d,%d,%ld,%lu\n", ...);
write(fd_csv_data, line, n);
As an alternative the safer snprintf() could be used.
With some extra checks:
#define LINESIZE 256
static char line[LINESIZE];
int n = sprintf(line, "%d,%d,%d,%ld,%lu\n", ...);
if (n > 0 && n < LINESIZE) {
write(fd_csv_data, line, n);
}
// else..
I'm trying to read data from a file into a buffer. The data in file is of 900K bytes. (seek to end of file and ftell()). Allocated the buffer in which the data is to be read of size 900K + 1 (to null terminate). My question is that fread() returns 900K but the I see the strlen(buffer) it shows lesser value and in the buffer at the last I can see something like ".....(truncated)". Why is this behavior? Is there a limit with fread() beyond which we cannot read into buffer and it will truncate it. Also why the return value of fread() says 900K even though actually it has read even less.?
strlen does something along these lines:
int strlen(char *str)
{
int len = 0;
while(*str++) len++;
return len;
}
If your file contains binary data (or if it's a text file with a UTF encoding and unused upper bytes) strlen is going to stop at the first 0x00 byte it encounters and return how many bytes into the file that was encountered. If you read a text file in a single-byte encoding like ANSI there won't be a null terminator and calling strlen will invoke undefined behavior.
If you want to determine how many bytes that fread successfully read out of the file, check its return value.1
If you want to determine the file size before reading a file, do this:
size_t len;
fseek(fp, 0, SEEK_END);
len = ftell(fp);
rewind(fp);
len will contain the file's size in bytes.
1: Assuming you called fread with parameter 2 set to 1 byte per element and didn't try to read more bytes than are actually in the file.
Your main question has already been answered, though it's worth notice that strlen is not designed to measure the size of an array but a NULL-terminated string. It probably prints a lower value because strlen returns the number of characters that appear before a null-char, so if you have nullchars ('\0') through your data, strlen will stop as soon as it finds one of them.
You should trust fread 's return value.
EDIT: as a note, fread MAY read less bytes than requested, and it can be caused by an error or an end of file. You can check it with ferror and feof, respectively.
I'm porting some code on an embedded platform that uses a C-like API. The original code uses fscanf() to read and parse data from files. Unfortunately on my API I don't have a fscanf() equivalent, so prior to the actual porting I'm trying to obtain the same behavior of fscanf() using fread() and vsscanf() (which I do have). I also have the equivalent of fseek() and ftell().
EDIT: please keep in mind that the access to the embedded filesystem is very limited (fread - fseek - ftell - fgetc - fgets), so I need a solution that works with strings in memory rather than accessing the file in some other way.
The code looks something like this:
int main()
{
[...] /* variable declarations and definitions */
do
{
read = wrapped_fscanf(pFile, "%d %s", &val, str);
} while (read == 2);
fclose(pFile);
return 0;
}
int wrapped_fscanf(FILE *f, const char *template, ...)
{
va_list args;
va_start(args, template);
char tmpstr[50];
fread(tmpstr, sizeof(char), sizeof(tmpstr), f);
int ret = vsscanf(tmpstr, template, args);
long offset = /* ??? */
fseek(f, offset, SEEK_CUR);
va_end(args);
return ret;
}
The problem is that fscanf() moves the pointer to the position in the file stream at the end of the match, whereas with fread() I'm reading a fixed amount of data (in this case 50 bytes) and I should find a way to move the pointer back to the end of the matched string.
Let's assume that the 50-char string I read from the file is the following:
12 bar 13 foo 56789012345678901234567890123456789
fscanf() would match the int 12 , the string bar and the pointer would point right after the "r" in "bar" so I can call it again and read 13 foo
On the other hand fread() puts the pointer after the last char in the 50-element sequence, which is wrong: I still have to read 13 foo but if I call wrapped_fscanf() again the pointer is in the 51st position.
I have to use fseek() to roll back to the end of the first match, but how do I do that? How do I calculate the value of offset ?
vsscanf() returns the number of matches, not the length of the string and I have no way of knowing how many whitespace charachters separate the elements of the match (or do I?)
I.e. I get the same outputs( {var,str,read} == {9,"xyz",2} ) with
9 xyz
and
9 xyz
Is there some trick that I'm not aware of or do I have to find another solution other than wrapping fscanf() with fread() vsscanf() ftell() and fseek()?
Thank you
Supposing that your vsscanf() implementation supports it, your substitute for fscanf() can append a %n field descriptor to the end of the provided format. As long as there is no failure prior to vsscanf() reaching that field, it will store the number of characters consumed up to that point in the corresponding argument. You could then use that result to reposition the stream appropriately. That would require a bit of varargs wrangling and probably some macro assistance, but I think it could be made to work.
You will need some intermediary buffering code, that will grab chunks of data (using fread), and scan your buffer for the pattern. if the pattern is found, truncate the buffer, if the pattern is not found, append some more data. this is effectively what fscanf will do.
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.
The user should input some file names in the command line and the program will read each file name from argv[] array. I have to perform error checking etc.
I want to read each filename. For example, if argv[2] is 'myfile.txt', the program should read the content of 'myfile.txt' and store value in char buffer[BUFSIZ] and then write the content of buffer into another file.
However before the content is written, the program should also write the name of the file and the size. Such that the file can be easily extracted later. A bit like the tar function.
The file I write the content of buffer, depending on the number of files added by user, should be a string like:
myfile.txt256Thisisfilecontentmyfile2.txt156Thisisfile2content..............
My question is
1) How do I write value of argv[2] into file using write() statement, as having problems writing char array, what should I put as (sizeof(?)) inside write(). see below as I don't know the length of the file name entered by the user.
2) Do I use the '&' to write an integer value into file after name, for example write 4 bytes after file name for the size of file
Here is the code I have written,
char buffer[BUFSIZ];
int numfiles=5; //say this is no of files user entered at command
open(file.....
lseek(fdout, 0, SEEK_SET); //start begging of file and move along each file some for loop
for(i=0-; ......
//for each file write filename,filesize,data....filename,filesize,data......
int bytesread=read(argv[i],buffer,sizeof(buffer));
write(outputfile, argv[i], sizeof(argv)); //write filename size of enough to store value of filename
write(outputfile, &bytesread, sizeof(bytesread));
write(outputfile, buffer, sizeof(buffer));
But the code is not working as I expected.
Any suggestions?
Since argv consists of null-terminated arrays, the length you can write is strlen(argv[2])+1 to write both the argument and null terminator:
size_t sz = strlen (argv[2]);
write (fd, argv[2], sz + 1);
Alternatively, if you want the length followed by the characters, you can write the size_t itself returned from strlen followed by that many characters.
size_t sz = strlen (argv[2]);
write (fd, &sz, sizeof (size_t));
write (fd, argv[2], sz);
You probably also need to write the length of the file as well so that you can locate the next file when reading it back.
1., You can write the string the following way:
size_t size = strlen(string);
write(fd, string, size);
However, most of the time it's not this simple: you will need the size of the string so you'll know how much you need to read. So you should write the string size too.
2., An integer can be written the following way:
write(fd, &integer, sizeof(integer));
This is simple, but if you plan to use the file on different architectures, you'll need to deal with endianness too.
It sounds like your best bet is to use a binary format. In your example, is the file called myfile.txt with a content length of 256, or myfile.txt2 with a content length of 56, or myfile.txt25 with a content length of 6? There's no way to distinguish between the end of the filename and the start of the content length field. Similarly there is no way to distinguish between the end of the content length and the start of the content. If you must use a text format, fixed width fields will help with this. I.e. 32 characters of filename followed by 6 digits of content length. But binary format is more efficient.
You get the filename length using strlen(), don't use sizeof(argv) as you will get completely the wrong result. sizeof(argv[i]) will also give the wrong result.
So write 4 bytes of filename length followed by the filename then 4 bytes of content length followed by the content.
If you want the format to be portable you need to be aware of byte order issues.
Lastly, if the file won't all fit in your buffer then you are stuffed. You need to get the size of the file you are reading to write it to your output file first, and then make sure you read that number of bytes from the first file into the second file. There are various techniques to do this.
thanks for replies guys,
I decided not to use (size_t) structure instead just assigned (int) and (char) types so I know exact value of bytes to read() out. ie I know start at beggining of file and read 4 bytes(int) to get value of lenght of filename, which I use as size in next read()
So, when I am writing (copying file exactly with same name) users inputted file to the output file (copied file) I writing it in long string, without spaces obviously just to make it readable here,
filenamesize filename filecontentsize filecontent
ie 10 myfile.txt 5 hello
So when come to reading that data out I start at begining of file using lseek() and I know the first 4 bytes are (int) which is lenght of filename so I put that into value int namelen using the read function.
My problem is I want to use that value read for the filenamesize(first 4 bytes) to declare my array to store filename with the right lenght. How do I put this array into read() so the read stores value inside that char array specified, see below please
int namelen; //value read from first 4 bytes of file lenght of filename to go in nxt read()
char filename[namelen];
read(fd, filename[namelen], namelen);//filename should have 'myfile.txt' if user entered that filename
So my question is once I read that first 4 bytes from file giving me lenght of filename stored in namelen, I then want to read namelen amount of bytes to give me the filename of originally file so I can create copied file inside directory?
Thanks