I just started learning C a little bit ago and I need help with a particular program. I need to read lines from a file (about 50 bytes at a time) and copy them over to a new file, including binary files. It's pretty simple but I'm having trouble on how exactly I should go about doing it. So far I have
while ( ) {
read (infile, buffer, 50);
if (r< 0) {
perror("cannot read infile");
exit (-1);
}
if (r==0) {
break;
}
write (outfile, buffer, r);
}
while I already open and close the files I will need. The loop should read the file 50 bytes at a time and write them to a new file, but I'm not sure what the condition of the while loop should be. I was thinking maybe taking the number of bytes in the file and make it so it will loop through the file as long as there are more bytes left. However, I am not entirely sure how to do that. Any help in the right direction?
You need to capture the return value of read:
r = read (infile, buffer, 50);
Given how the loop is set up, you could use the always-true condition: while(1) (since you already break when you reach the end of the file)
while (1) {
int r = read (infile, buffer, 50);
if (r< 0) {
perror("cannot read infile");
exit (-1);
}
if (r==0) {
break;
}
if(write (outfile, buffer, r) != r)
printf("write error\n");
}
Firstly, you can change (r == 0) to (r < 50) because the last line probably won't be a full 50 bytes. If it is, the next run through will be 0 anyway, which will still evaluate to true. And you never actually capture r as a return value from read().
For your actual question, you could do it in a variety of ways. Currently, you could write
while(1){
r = read(infile, buffer, 50);
if ( r < 50) {
...
break;
}
}
will work just fine. The break statement will exit the loop when the r < 50 condition is satisfied, implying no bytes were read.
For code that is more clear and readable, you could use an int as a boolean like this:
int FileDone = 0;
while(!FileDone){
...
if ( r < 50) {
FileDone = 1
}
}
or maybe like this:
int r = 1;
while (r > 0){
r = read(infile, buffer, 50);
if (r < 0) {
perror("cannot read infile");
exit (-1);
}
write (outfile, buffer, r);
}
Related
So, I'm writing this simple HTTP client in C and I seem to be stuck on this problem - how do I strip the HTTP headers from the response? After all, if I get a binary file I can't just write the headers out to my output file. I can't seem to go in once the data is already written to a file because linux screams when you try to even view the first few lines of a binary file, even if you know they're just text HTTP headers.
Now, here's the rub (well, I suppose the whole thing is a rub). Sometimes the whole header doesn't even in come in on the first response packet, so I can't even guarantee that we'll have the whole header in our first iteration (that is, iteration of receiving an HTTP response. We're using recv(), here), which means I need to somehow... well, I don't even know. I can't seem to mess with the data once it's already written to disk, so I need to deal with it as it's coming in, but we can't be sure how it's going to come in, and even if we were sure, strtok() is a nightmare to use.
I guess I'm just hoping someone out there has a better idea. Here's the relevant code. This is really stripped down, I'm going for MCVE, of course. Also, you can just assume that socket_file_descriptor is already instantiated and get_request contains the text of our GET request. Here is it:
FILE* fp = fopen("output", "wb"); // Open the file for writing
char buf[MAXDATASIZE]; // The buffer
size_t numbytes; // For the size of the response
/*
* Do all the socket programming stuff to get the socket file descriptor that we need
* ...
* ...
*/
send(socket_file_descriptor, get_request, strlen(get_request), 0); // Send the HTTP GET request
while ((numbytes = recv(socket_file_descriptor, buf, MAXDATASIZE - 1, 0)) > 0) {
/* I either need to do something here, to deal with getting rid of the headers before writing to file */
fwrite(buf, 1, numbytes, fp); // Write to file
memset(buf, 0, MAXDATASIZE); // This just resets the buffer to make room for the next packet
}
close(s);
fclose(fp);
/* Or I need to do something here, to strip the file of its headers after it's been written to disk */
So, I thought about doing something like this. The only thing we know for sure is that the header is going to end in \r\n\r\n (two carriage returns). So we can use that. This doesn't really work, but hopefully you can figure out where I'm trying to go with it (comments from above removed):
FILE* fp = fopen("output", "wb");
char buf[MAXDATASIZE];
size_t numbytes;
int header_found = 0; // Add a flag, here
/* ...
* ...
*/
send(socket_file_descriptor, get_request, strlen(get_request), 0);
while ((numbytes = recv(socket_file_descriptor, buf, MAXDATASIZE - 1, 0)) > 0) {
if (header_found == 1) { // So this won't happen our first pass through
fwrite(buf, 1, numbytes, fp);
memset(buf, 0, MAXDATASIZE);
}
else { // This will happen our first pass through, maybe our second or third, the header doesn't always come in in full on the first packet
/* And this is where I'm stuck.
* I'm thinking about using strtok() to parse through the lines, but....
* well I just can't figure it out. I'm hoping someone can at least point
* me in the right direction.
*
* The point here would be to somehow determine when we've seen two carriage returns
* in a row and then mark header_found as 1. But even if we DID manage to find the
* two carriage returns, we still need to write the remaining data from this packet to
* the file before moving on to the next iteration, but WITHOUT including the
* header information.
*/
}
}
close(s);
fclose(fp);
I've been staring at this code for three days straight and am slowly losing my mind, so I really appreciate any insight anyone is able to provide. To generalize the problem, I guess this really comes down to me just not understanding how to do text parsing in C.
The second self-answer is better than the first one, but it still could be made much simpler:
const char* pattern = "\r\n\r\n";
const char* patp = pattern;
while ((numbytes = recv(socket_file_descriptor, buf, MAXDATASIZE - 1, 0)) > 0) {
for (int i = 0; i < numbytes; i++) {
if (*patp == 0) {
fwrite(buf + i, 1, numbytes - i, fp);
break;
}
else if (buf[i] == *patp) ++patp;
else patp = pattern;
}
/* This memset isn't really necessary */
memset(buf, 0, MAXDATASIZE);
}
That looks like a general solution, but it's not really: there are values for pattern for which it might fail to see a terminator under particular circumstances. But this particular pattern is not problematic. You might want to think about what sort of pattern would cause a problem before taking a look at the more general solution.
So, I know this is not the most elegant way to go about this, but... I did get it. For anyone who finds this question and is curious about at least an answer, here it is:
int count = 0;
int firstr_found = 0;
int firstn_found = 0;
int secondr_found = 0;
int secondn_found = 0;
FILE* fp = fopen("output", "wb");
char buf[MAXDATASIZE];
size_t numbytes;
int header_found = 0;
/* ...
* ...
*/
send(socket_file_descriptor, get_request, strlen(get_request), 0);
while ((numbytes = recv(socket_file_descriptor, buf, MAXDATASIZE - 1, 0)) > 0) {
if (header_found == 1) {
fwrite(buf, 1, numbytes, fp);
}
else {
// These buf[i]'s are going to return as integers (ASCII)
// \r is 13 and \n is 10, so we're looking for 13 10 13 10
// This also needs to be agnostic of which packet # we're on; sometimes the header is split up.
for (int i = 0; i < numbytes; i++) {
if (firstr_found == 1 && firstn_found == 1 && secondr_found == 1 && secondn_found == 1) { // WE FOUND IT!
header_found = 1;
// We want to skip the parts of the buffer we've already looked at, that's header, and our numbytes will be decreased by that many
fwrite(buf + i, 1, numbytes - i, fp);
break;
}
if (buf[i] == 13 && firstr_found == 0) { // We found our first \r, mark it and move on to next iteration
firstr_found = 1;
continue;
}
if (buf[i] == 10 && firstr_found == 1 && firstn_found == 0) { // We found our first \n, mark it and move on
firstn_found = 1;
continue;
}
else if (buf[i] != 13 && buf[i] != 10) { // Think about the second r, it'll ignore the first if, but fail on the second if, but we don't want to jump into this else block
firstr_found = 0;
firstn_found = 0;
continue;
}
if (buf[i] == 13 && firstr_found == 1 && firstn_found == 1 && secondr_found == 0) {
secondr_found = 1;
continue;
}
else if (buf[i] != 10) {
firstr_found = 0;
firstn_found = 0;
secondr_found = 0;
continue;
}
if(buf[i] == 10 && firstr_found == 1 && firstn_found == 1 && secondr_found == 1 && secondn_found == 0) {
secondn_found = 1;
continue;
}
}
}
memset(buf, 0, MAXDATASIZE);
count++;
}
close(s);
fclose(fp);
Adding another answer because, well I suppose I think I'm clever. Thanks to #tadman for the idea of a counter. Look here (I'm going to shave off a lot of the bloat and just do the while loop, if you've looked at my other code blocks you should be able to see what I mean here) ...
/* ...
* ...
*/
int consec_success = 0;
while ((numbytes = recv(socket_file_descriptor, buf, MAXDATASIZE - 1, 0)) > 0) {
if (header_found == 1) {
fwrite(buf, 1, numbytes, fp);
}
else {
for (int i = 0; i < numbytes; i++) {
if (consec_success == 4) {
header_found = 1;
fwrite(buf + i, 1, numbytes - i, fp);
break;
}
if (buf[i] == 13 && consec_success % 2 == 0) {
consec_success++;
}
else if (buf[i] == 10 && consec_success % 2 == 1) {
consec_success++;
}
else {
consec_success = 0;
}
}
}
memset(buf, 0, MAXDATASIZE);
}
/* ...
* ...
*/
So recently for a course project, I decided to make myself a program that could transfer a file across a lan network and integrate it into the linux operating system (In this case, all I did was add it to the context menu) using a socket server.
The way it works is essentially,
Server is waiting.
Client connects.
Client sends a message of 1024 length with the first 4 characters reserved
The first 4 characters are used to store an int which will state the length of the message
server recieves them, writes them, then waits for the next block
when the server recieves a message where the length is 0
it ends the transfer and closes the files
This works for text files flawlessly. With improvements on my last code thanks to helpful feedback, I've managed to create something where the OS actually recognizes the file extension, regardless of the type. However for things like pngs they show up black, for exe's they immediately segfault.
What can I change in my reading and writing to get this to work regardless of file type? I'm not sure where to go, as what I have should work
Additional info: I am coding in C. To open the file I use fopen, fgetc and fputc.
Here is an exert from my code for my sever:
while (1){
n = read(newsockfd,message,1024);
if (n < 0) {
fclose(fptr2);
error("ERROR reading from socket");
}
//The first 4 bytes/characters are used to store the length.
//I read them by getting a pointer to the first char and then reading it as
//an int by casting it. This works with no problem
char *p=&message;
int *p2=(int*)p;
int length=*p2;
//Checks if the length is 0, if so, exit
if (length==0)
break;
//writes to the file
for (int i=4;i<length;i++){
fputc(message[i], fptr2);
}
n = write(newsockfd,"Ready",5);
if (n < 0)
error("ERROR writing to socket");
bzero(message,255);
}
fclose(fptr2);
//n = write(newsockfd,"I got your message",18);
//if (n < 0) error("ERROR writing to socket");
printf("Done.\n");
return 0;
}
Exert from my client, which reads the file in and then sends it.
while (finished!=0&&c!=EOF)
{
for (int i =4;i<1024;i++)
{
if (c==EOF)
{
char* p=&message;
int* pi=(int*)p;
*pi=i;
finished=0;
//printf("length is:%d\n",i);
break;
}
//printf("%c",c);
message[i]=c;
//fputc(c, fptr2);
c = fgetc(fptr1);
}
if (finished!=0)
{
char* p=&message;
int* pi=(int*)p;
*pi=1024;
}
n = write(sockfd,message,1024);
if (n < 0)
{
fclose(fptr1);
error("ERROR writing to socket");
}
bzero(message,1024);
//reading
n = read(sockfd,buffer,255);
if (n < 0)
error("ERROR reading from socket");
}
0x00 is a valid character for a binary file, so you can't "stop" when you see one [like you can for a string].
You are using the first char of a packet as an EOF marker (i.e. non-zero means valid data and zero means EOF). But, note that the first data char in a packet could be zero, so you have to use a one byte "header" that doesn't have data chars in it, merely the "stop" flag char [if you will]:
while (1) {
// Reading
n = read(newsockfd, message, 1023);
if (n < 0) {
fclose(fptr2);
error("ERROR reading from socket");
}
// Checks if the first character is null, if so, exit
if (message[0] == 0)
break;
// writes to the file
// NOTE: now the data starts at offset 1!
int i = 1;
for (; i < n; ++i) {
fputc(message[i], fptr2);
}
i = 0;
n = write(newsockfd, "Ready", 5);
if (n < 0)
error("ERROR writing to socket");
bzero(message, 1023);
}
fclose(fptr2);
But, a simpler way is to just read until the length comes back zero:
while (1) {
// Reading
n = read(newsockfd, message, 1024);
// end of data
if (n == 0)
break;
// error
if (n < 0) {
fclose(fptr2);
error("ERROR reading from socket");
}
// writes to the file
fwrite(message,1,n,fptr2);
i = 0;
n = write(newsockfd, "Ready", 5);
if (n < 0)
error("ERROR writing to socket");
bzero(message, 1023);
}
fclose(fptr2);
In short, the most important change to get this to work is changing when the program stops reading. EOF is simply -1, in a text file, this is no problem, however in other files this value can be found potentially anywhere. In order to read in the characters properly you must first get the file length, then simply read characters until you reach that.
fseek(fptr1, 0L, SEEK_END);
int sz = ftell(fptr1);
rewind(fptr1);
int count=0;
while (count!=sz)
{
//other code left out for simplicity
c = fgetc(fptr1);
count++;
}
With this change, my program works properly.
This is a c code to create a copy of a file or directory, compiled using gcc in fedora 19. It runs but it doesn't stop and I can see the new file created keeps on increasing in size ridiculously. What is wrong with this code?
#include<fcntl.h>
#include<stdio.h>
#include<unistd.h>
char buffer[2048];
int version = 1;
int main(int argc, char *argv[])
{
void copy (int, int);
int fdold, fdnew;
if (argc != 3)
{
printf("insufficient arguments !!! \n");
return (1);
}
fdold = open(argv[1], O_RDONLY);
if (fdold == -1)
{
printf("cannot open file %s\n",argv[1]);
return (1);
}
fdnew = creat(argv[2],0666);
if (fdnew == -1)
{
printf("cannot create file %s\n",argv[2]);
return (1);
}
copy (fdold, fdnew)
return (0);
}
void copy (int fdold, int fdnew)
{
int count;
count = read(fdold, buffer, sizeof(buffer));
while (count > 0)
write(fdnew, buffer, count);
}
You never update count and you keep writing the same data over and over again. In this code:
count = read(fdold, buffer, sizeof(buffer));
while (count > 0)
write(fdnew, buffer, count);
You read from the file descriptor once, pulling in count bytes, and while it's greater than 0 (which is presumably is), you keep writing the buffer out to the new file. You never read any more data from the old file. If you can see the file getting bigger and bigger, you might also be able to see (depending on how the content gets flushed to disk) the same content repeated over and over again.
What you actually need to be doing is something more like this:
while there is data to read from the old file
read it into a buffer
while there is data in the buffer to write to the new file
write data from the buffer into the new file
In slightly less pseudo-codish, but highly untested form, I think you'd be looking for something sort of like this:
int count = 0;
int written;
while ( 0 < (count = read(fdold, buffer, sizeof(buffer))) ) {
written = 0;
while ( count > (written += write(fdnew, buffer+written, count-written))) );
}
The outer loop makes sure that you read until there's nothing left to read, and the inner while make sure that you call write until written is as big as count, that is, until you've written all the bytes that you read. This is “clever” code, but it's actually too clever; you actually need to check whether written is -1, or else you'll start doing some strange things.
Something with more error checking, and hopefully more idiomatic, might be:
for ( int count_in = -1; count_in != 0; ) {
if ( -1 == (count_in = read(fd, buf, bufsize))) {
perror("Problem reading from file");
exit(-1);
}
else {
for ( int count_out = 0, out = 0; count_out < count_in; count_out += out ) {
if ( -1 == (out = write(fd, buf+count_out, count_in-count_out)) ) {
perror("Problem writing to file");
exit(-1);
}
}
}
}
I am trying to read data from a socket. For some reason I cannot understand, it seems the read function goes into an infinite loop. The reason that's what I think happens is because while using eclipse to debug, the debugger does not get past the read function. Also, when I run the program with terminal, it runs forever. Help!!
Additional info: Running on Linux, and, not sure if has anything to do with this issue, but I create threads in the program.
Another something I think I should mention: The first time read() is called it works as expected and reads the whole message that's in the socket. The problem starts when read() is called again the second time (when there is nothing left to read). I expected that read would return 0 which would end the function, but instead, read goes into an infinite loop.
Here's where it's all happening:
read_write_res block_read_reply(int fd, void* buf, int max, int* read_size) {
int flag = 1;
if (read_size != NULL)
*read_size = 0;
int i;
while (1) {
i = read(fd, buf, max); /* HERE is where the debbuger gets stuck */
if (i == 0 && flag == 1) //nothing to read
continue;
if (i == 0 && flag == 0)
return READ_WRITE_SUCCESS;
if (i < 0){
return READ_WRITE_FAILURE;
if (i > 0 && read_size != NULL)
*read_size += i;
}
flag = 0;
max -= i;
buf = (char*) (buf) + i;
}
return READ_WRITE_SUCCESS;
}
If read returns 0, it means you have reached end-of-file status. For a socket or pipe, this means there will be nothing more to read. EVER. So performing continue; in this case is definitely not what you want to be doing.
From the below piece of code, why I am getting Reading Socket for response
int Read(int sock, char *p, int size)
{
int remain, read=0;
remain = size;
while (remain > 0 ) {
if ((read = recv(sock, p, remain, 0)) < 0) {
/* Error */
return(read);
} else if (read == 0 || *p == 0x0a) {
/* EOF */
break;
}
remain -= read;
p += read;
}
return(size - remain);
}
while (!done)
{
printf("***Reading Socket for response***");
rsplen= Read(myVsHandle.sock,(char *)encXMLResponse,MAX_RSP_LEN);
if (rsplen < 0 )
{
printf("Internal Communication Error");
return -1;
}
else if (rsplen >0)
printf("Revieved response");
done++;
return 0;
else if (rsplen == 0)
{
printf("Reading socket");
}
You are waiting for MAX_RSP_LEN bytes to be read - is there that many bytes to be read? Maybe your process is stuck in a blocking read().
Also depending on the sort of socket you are recv()ing from, there is no guarantee on the amount of data you will read, so specifically looking for a value 0x0a may not work.
Your problem could be that you are not ending your output with a newline. Try ending your outputs with a newline (\n). stdout is line buffered, so you may not see anything for a long time if you don't output a newline.
Another possibility is that you don't return from Read() unless you read the specified number of bytes. Depending upon the value of MAX_RSP_LEN, and the amount of data available, Read() may wait forever.
Also, your test: *p == 0x0a looks suspicious. What are you testing here?
Edit: There is another "bug":
else if (rsplen >0)
printf("Revieved response");
done++;
return 0;
else...
You are missing curly braces. In the current form, the code shouldn't compile. Please post actual code.
This:
if ((read = recv(sock, p, remain, 0)) < 0) {
Should be
if ((read = recv(sock, p, remain, 0)) > 0) { // Greater then 0, because recv returns the number of bytes received if successful, if it fails -1.
You're missing curly braces around the:
else if(rsplen > 0)
... statements
It should be:
...
}else if (rsplen >0){
printf("Revieved response");
done++;
return 0;
} ...