How to read a large file with function read() in C - c

Aloha,
I'm new here, so please take it easy on me.
I'm trying to read a file with function read() and then write() to a file or a file descriptor. My function successfully reads a file, but a problem occurs when I try to read a larger file(in my example size of 40,000 bytes).
I think that I must write a while loop, which will be reading until the end of a file, but I am stuck on the idea of how to..
(I open a file or file descriptor in main of the program)
My function( also convert binary input char data and writes to the ASCII) :
void function(int readFrom,int writeOn){
char buffer[100];
int x = read(readFrom, buffer, sizeof(buffer));
int size= x/8;
int i;
for(i=0; i<size; i++){
char temp[sizeof(int)-1];
sprintf(temp,"%d",buffer[i];
write(writeOn, temp, sizeof(temp));
}
}

You need to check return value of functions read and write. They return the number of bytes read/written that may be less than the number that you passed as third argument. Both read and write must be done in a loop like:
int bytesRead = 0;
while (bytesRead < sizeof(buffer)) {
int ret = read(readFrom, buffer + bytesRead, sizeof(buffer) - bytesRead);
if (ret == 0)
break; / * EOF */
if (ret == -1) {
/* Handle error */
}
bytesRead += ret;
}

You use sprintf() to convert characters from buffer into a very small buffer temp. On most current systems, int is 4 bytes, so your printf causes buffer overflows for char values greater than 99 (ASCII letter 'c'). Note that char can be signed by default, so negative values less than -99 will require 5 bytes for the string conversion: 3 digits, a minus sign and a null terminator.
You should make this buffer larger.
Furthermore, I don't understand why you only handle x/8 bytes from the buffer read by the read() function. The purpose of your function is obscure.

Related

C write and read single character

I'm currently working with processes, and encountered a problem while reading and writing char to a file.
The idea is we have couple of processes which should read an integer from file, increment it and write back. Here is my attempt: (i wont include error checking)
...
char n;
char buff[5];
int number;
...
read(my_desc, &n, 1);
number = (int)n;
number++;
sprintf(buff, "%4d", number);
write(my_desc, buff, sizeof(buff));
...
The file is just plain
0
But the output seems to be not correct (almost always garbage).
I already read write and read manuals but im clueless. I've checked some topics on read and write functions here on stack overflow, but most of them either don't work for me or i struggle with implementation.
Thanks in advance.
It appears that you are reading a single character, taking the ASCII code of that character and converting that number to a 4-character string, and then writing those 4 characters and the terminating null character back to the file.
According to the information that you provided in the comments section, this is not intended. If I understand you correctly, you rather want to
read the entire file as a string,
convert that string to a number,
increment that number,
convert that number back to a string and
overwrite the entire file with that string.
Step #1 can be accomplished with the function read. However, you should read in the whole file instead of only a single character.
Step #2 can be accomplished by using the function strtol.
Step #3 is trivial.
Step #4 can be accomplished using the function snprintf.
Step #5 can be accomplished by rewinding the file using the function lseek, and then using the function write.
I am assuming that the number represented in the file is in the respresentable range of a long int, which is -9,223,372,036,854,775,808 to +9,223,372,036,854,775,807 on most POSIX platforms. This means that the length of the string can be up to 19 characters, 20 including the terminating null character. That is why I am using a buffer size of 20.
char buffer[20], *p;
ssize_t bytes_read;
long num;
bytes_read = read( my_desc, buffer, (sizeof buffer) - 1 );
if ( bytes_read <= 0 )
{
//TODO: handle input error
}
//add null terminating character to string
buffer[bytes_read] = '\0';
//attempt to convert the string to a number
num = strtol( buffer, &p, 10 );
//check for conversion error
if ( p == buffer )
{
//TODO: handle conversion error
}
//increment the number
num++;
//write incremented number to buffer
snprintf( buffer, sizeof buffer, "%ld", num );
//rewind file
lseek( my_desc, 0, SEEK_SET );
//write buffer to file
write( my_desc, buffer, strlen(buffer) );
Note that I have not tested this code.
Also note that this program assumes that the input file does not contain any leading zeros. If the file contains the text string "003", then this program will overwrite the first character with a 4, but leave the remaining characters in the file intact. If this is an issue, then you will have to add a call to ftruncate to truncate the file.

Need a function like read() that reads integer data into buffer and obtains same buffer value as read()

How can I convert integer into value/encoded character same as the buffer obtained with read(0,buff,nbytes) when I stdin same integer? Am attempting to write something similar like read() but with integer data in place of file descriptor argument that gets read into buffer.
like_read(int data,void *buff,size_t nbytes);
It should be similar to read() in the sense that it should read into buffer same value that read(0,buff,nbytes) would from stdin into its buffer. when I provide integer address directly as buffer without first using read(0,buff,nbytes), for example
int Integer=25
int nbytes=2;
int rlen,wlen,wlen1;
int fd = open("ttyusb.txt", O_RDWR | O_CREAT,0777);
wlen = write(fd, &Integer, nbytes);
wlen1 = write(1, &Integer, nbytes);//for stdout
close(fd);
Expected output
25
Actual output/file content is some encoded character
,its not giving me the desired result as first using read() to read integer into buffer from stdin and then writing that buffer into file using write() like:
int Integer;
int nbytes=2;
int rlen,wlen;
int fd = open("ttyusb.txt", O_RDWR | O_CREAT,0777);
rlen = read(0, &Integer, nbytes);
wlen = write(fd, &Integer, nbytes);
wlen1 = write(1, &Integer, nbytes);//for stdout
close(fd);
Stdin
25
Expected output
25
Actual output/file content
25
when I print buffer value after read(0, buffer, nbytes), it gives some encoded value:
int Integer, nbytes=2;
int fd = open("ttyusb.txt", O_RDWR | O_CREAT,0777);
rlen = read(0, &Integer, nbytes);
wlen = write(fd, &Integer, nbytes);
wlen1 = write(1, &Integer, nbytes);
printf("\nInteger buffer value %d\n",Integer);
close(fd);
stdin 0 prints "Integer buffer value 2608", stdin 1 prints "Integer buffer value 2609", stdin 2 prints "Integer buffer value 2610",.....stdin 9 prints "Integer buffer value 2617"...
what encoding is read() using to convert integer values and how can I do that conversion without read()?
I'm not really sure what you are trying to achieve, but my interpretation is that you are trying to copy the bytes of an int into a buffer. That could be implemented something like this:
#include <string.h>
void like_read(int data, void *buff, size_t nbytes)
{
size_t n;
/* Limit number of bytes to copy from data variable. */
n = sizeof(data);
if (n < nbytes)
n = nbytes;
/* Copy bytes from data variable to the buffer. */
memcpy(buff, &data, n);
/* If destination buffer is larger than data variable, ... */
if (n < nbytes) {
/* ... set the remaining bytes in the destination to all zero. */
memset((char *)buff + n, 0, nbytes - n);
}
}
what encoding is read() using to convert integer values and how can I do that conversion without read()?
read() just reads raw data bytes from the file descriptor, without any conversion, at least on POSIX type systems such as Unix and Linux. Some other operating systems such as Microsoft Windows allow files to be opened with special flags that do some conversion on the raw data bytes, mostly for handling text files with different encodings.
The memcpy() call in the like_read function above copies raw data bytes from one memory location to another without any conversion.
EDIT
I am still not sure what you (OP) are trying to achieve, but based on your comments below, perhaps you are after a function that "prints" the integer value as decimal ASCII characters to the buffer with a newline, but truncates to a specified number of bytes. This can nearly be done by snprintf() but that function always writes a terminating null byte to the buffer (if the buffer size is at least 1), so isn't quite what I think you want. Therefore, snprintf() can be used to print the integer value to an intermediate buffer of sufficient size and then memcpy the result to the specified buffer. Here is an implementation of that:
#include <string.h>
#include <stdio.h>
void like_read(int data, void *buff, size_t nbytes)
{
char tempbuf[24]; /* ought to be large enough... */
size_t n;
/* Print data value to temporary buffer. */
n = snprintf(tempbuf, sizeof(tempbuf), "%d\n", data);
/* n is number of bytes that would be printed to tempbuf if room. */
/* Limit number of bytes to copy to size of temporary buffer. */
if (n > sizeof(tempbuf))
n = sizeof(tempbuf);
/* Limit number of bytes to copy to nbytes. */
if (n > nbytes)
n = nbytes;
/* Copy bytes from tempbuf to the buffer. */
memcpy(buff, tempbuf, n);
/* If destination buffer is larger than n, ... */
if (n < nbytes) {
/* ... set the remaining bytes in the destination to all zero. */
memset((char *)buff + n, 0, nbytes - n);
}
}

Difference between specifications of fread and fgets?

What is the difference between fread and fgets when reading in from a file?
I use the same fwrite statement, however when I use fgets to read in a .txt file it works as intended, but when I use fread() it does not.
I've switched from fgets/fputs to fread/fwrite when reading from and to a file. I've used fopen(rb/wb) to read in binary rather than standard characters. I understand that fread will get /0 Null bytes as well rather than just single lines.
//while (fgets(buff,1023,fpinput) != NULL) //read in from file
while (fread(buff, 1, 1023, fpinput) != 0) // read from file
I expect to read in from a file to a buffer, put the buffer in shared memory, and then have another process read from shared memory and write to a new file.
When I use fgets() it works as intended with .txt files, but when using fread it adds a single line from 300~ characters into the buffer with a new line. Can't for the life of me figure out why.
fgets will stop when encountering a newline. fread does not. So fgets is typically only useful for text files, while fread can be used for both text and binary files.
From the C11 standard:
7.21.7.2 The fgets function
The fgets function reads at most one less than the number of characters specified by n from the stream pointed to by stream into the array pointed to by s. No additional characters are read after a new-line character (which is retained) or after end-of-file. A null character is written immediately after the last character read into the array.
7.21.8.1 The fread function
The fread function reads, into the array pointed to by ptr, up to nmemb elements whose size is specified by size, from the stream pointed to by stream. For each object, size calls are made to the fgetc function and the results stored, in the order read, in an array of unsigned char exactly overlaying the object. The file position indicator for the stream (if defined) is advanced by the number of characters successfully read. If an error occurs, the resulting value of the file position indicator for the stream is indeterminate. If a partial element is read, its value is indeterminate.
This snippet maybe will make things clearer for you. It just copies a file in chunks.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char ** argv)
{
if(argc != 3) {
printf("Usage: ./a.out src dst\n");
printf("Copies file src to dst\n");
exit(EXIT_SUCCESS);
}
const size_t chunk_size = 1024;
FILE *in, *out;
if(! (in = fopen(argv[1], "rb"))) exit(EXIT_FAILURE);
if(! (out = fopen(argv[2], "wb"))) exit(EXIT_FAILURE);
char * buffer;
if(! (buffer = malloc(chunk_size))) exit(EXIT_FAILURE);
size_t bytes_read;
do {
// fread returns the number of successfully read elements
bytes_read = fread(buffer, 1, chunk_size, in);
/* Insert any modifications you may */
/* want to do here */
// write bytes_read bytes from buffer to output file
if(fwrite(buffer, 1, bytes_read, out) != bytes_read) exit(EXIT_FAILURE);
// When we read less than chunk_size we are either done or an error has
// occured. This error is not handled in this program.
} while(bytes_read == chunk_size);
free(buffer);
fclose(out);
fclose(in);
}
You mentioned in a comment below that you wanted to use this for byteswapping. Well, you can just use the following snippet. Just insert it where indicated in code above.
for(int i=0; i < bytes_read - bytes_read%2; i+=2) {
char tmp = buffer[i];
buffer[i] = buffer[i+1];
buffer[i+1] = tmp;
}

Reading fully from file descriptor of unknown size

I have used pipe and I have to read from this pipe. But problem is this:
ssize_t read(int fd, void *buf, size_t count)
I don't know how many characters is stored in the reading end of pipe, so I can't assign some number to count. I need this to store in the buffer.
How can I number of characters stored in this pipe?
With regards
I don't know how many characters is stored in the reading end of pipe
Don't worry about it. There are advantages (e.g. atomicity) to not trying to write/read more than PIPE_BUF bytes at shot. In reality you will probably get a bunch of short reads anyway.
#define READ_BUFFER_SIZE PIPE_BUF
unsigned char mybuffer[READ_BUFFER_SIZE];
ssize_t bytesread = 1;
while ((bytesread = read(pipefd, mybuffer, READ_BUFFER_SIZE)) > 0)
{
concat to bigger buffer, realloc if necessary
}
Just use a reasonably sized buffer, and read as much as you can. Repeat that. The function returns the number of bytes read.
You need not know before hand how many bytes are there and pass that as as a value for count.
You can define buffer of maximum data size that you can expect and read from the fd until data is present.
char buf[MAX_DATA_SIZE] = {0};
bytes_read = 0;
while(n > 0)
{
n = read(fd,buf+bytes_read,MAX_DATA_SIZE)
bytes_read = bytes_read + n;
}
You can simply request the number of characters up to the size of your buffer, and do so repeatedly in a loop, e.g:
char* buf = malloc(1024);
do {
bytes_read = read(fd, buf, 1024);
// store buf somewhere else so you can use it in the next iteration
} while (bytes_read > 0)
free(buf);

Arduino serial message with unknown length

How can I store the result of Serial.readBytesUntil(character, buffer, length) in a buffer while I don't know the length of the incoming message ?
Here is a little code that makes use of realloc() to keep growing your buffer. You will have to free() when you're done with buf.
int length = 8;
char * buf = malloc(length);
int total_read = 0;
total_read = Serial.readBytesUntil(character, buf, length);
while(length == total_read) {
length *= 2;
buf = realloc(buf, length);
// Bug in this line:
// total_read += Serial.readBytesUntil(character, buf+total_read, length);
// Should be
total_read += Serial.readBytesUntil(character, buf+total_read, length-total_read);
}
*Edit: fixed a bug where readBytesUntil would have read off the end of buf by reading length bytes instead of length-total_read bytes.
make the buffer big enough for the message. Don't know the maximum length of the message? Use length to control the characters read, then continue reading until character encountered.
int bytesRead = Serial.readBytesUntil(character, buffer, length);
You could create a buffer that is just smaller than the remaining RAM and use that. The call to find the remaining ram (as I've posted elsewhere) is:
int freeRam () {
extern int __heap_start, *__brkval;
int v;
int fr = (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
Serial.print("Free ram: ");
Serial.println(fr);
}
Regardless, you should make sure you only read into as much RAM as you actually have.
One answer is that when a program reads serial bytes it typically does NOT store them verbatim. Rather, the program examines each byte and determines what action to take next. This logic is typically implemented as Finite State Machine.
So, what does your specific serial stream represent? Can it be analyzed in sequential chunks? For example: "0008ABCDEFGH" says that 8 chars follow the 4 character length field. In this silly example your code would read 4 chars, then know how much space to allocate for the rest of the serial stream!

Resources