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.
Related
I want to print the contents of a .txt file to the command line like this:
main() {
int fd;
char buffer[1000];
fd = open("testfile.txt", O_RDONLY);
read(fd, buffer, strlen(buffer));
printf("%s\n", buffer);
close(fd);
}
The file testfile.txt looks like this:
line1
line2
line3
line4
The function prints only the first 4 letters line.
When using sizeof instead of strlen the whole file is printed.
Why is strlen not working?
It is incorrect to use strlen at all in this program. Before the call to read, the buffer is uninitialized and applying strlen to it has undefined behavior. After the call to read, some number of bytes of the buffer are initialized, but the buffer is not necessarily a proper C string; strlen(buffer) may return a number having no relationship to the amount of data you should print out, or may still have UB (if read initialized the full length of the array with non-nul bytes, strlen will walk off the end). For the same reason, printf("%s\n", buffer) is wrong.
Your program also can't handle files larger than the buffer at all.
The right way to do this is by using the return value of read, and write, in a loop. To tell read how big the buffer is, you use sizeof. (Note: if you had allocated the buffer with malloc rather than as a local variable, then you could not use sizeof to get its size; you would have to remember the size yourself.)
#include <unistd.h>
#include <stdio.h>
int main(void)
{
char buf[1024];
ssize_t n;
while ((n = read(0, buf, sizeof buf)) > 0)
write(1, buf, n);
if (n < 0) {
perror("read");
return 1;
}
return 0;
}
Exercise: cope with short writes and write errors.
When using sizeof instead of strlen the whole file is printed. Why is
strlen not working?
Because how strlen works is it goes through the char array passed in and counts characters till it encounters 0. In your case, buffer is not initialized - hence it will try to access elements of uninitialized array (buffer) to look for 0, but reading uninitialized memory is not allowed in C. Actually you get undefined behavior.
sizeof works differently and returns the number of bytes of the passed object directly without looking for a 0 inside the array as strlen does.
As correctly noted in other answers read will not null terminate the string for you so you have to do it manually or declare buffer as:
char buffer[1000] = {0};
In this case printing such buffer using %s and printf after reading the file, will work, only assuming read didn't initialize full array with bytes of which none is 0.
Extra:
Null terminating a string means you append a 0 to it somewhere. This is how most of the string related functions guess where the string ends.
Why is strlen not working?
Because when you call it in read(fd, buffer, strlen(buffer));, you haven't yet assigned a valid string to buffer. It contains some indeterminate data which may or may not have a 0-valued element. Based on the behavior you report, buffer just so happens to have a 0 at element 4, but that's not reliable.
The third parameter tells read how many bytes to read from the file descriptor - if you want to read as many bytes as buffer is sized to hold, use sizeof buffer. read will return the number of bytes read from fd (0 for EOF, -1 for an error). IINM, read will not zero-terminate the input, so using strlen on buffer after calling read would still be an error.
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.
I am trying to write an algorithm that takes input from a file and builds what is called an "s1 record". (The functionality of this function is not important) Depending on the command line arguments, the program will set the inputFile to the specified file, or stdin if no file is provided.
The algorithm needs to be structured in a way that can handle both file patterns.
The idea of this is to take FILE* data and read it into a buffer of size 16 bytes. Every 16 bytes of data, an s1 record will be built. As long as there are 16 bytes to read then it works fine and dandy. Once there is a line with less than 16 bytes, it doesn't create an s1 record.
Ive tested the output and these are some of the things I noticed:
When I run the program using "stdin", I am prompted for user input. I enter 20 characters (which should print 16 in 1 srecord, and 4 in another) and my output is as follows:
12345678901234567890
Buffer: 1234567890123456
S113000031323334353637383930313233343536AA
When I run the program using a file (record.dat) with one single line with the characters of the alphabet on it, I get this:
Buffer: ABCDEFGHIJKLMNOP
Buffer: QRSTUVWXYZKLMNOP
This is not valid either, as it prints the "KLMNOP" at the end of the line as well.
My question is: How can I structure this to accept the input from either a file or stdin using the same algorithm, and what exactly am I doing wrong in my algorithm? I have tried providing all the useful information I can, and can specify more detail if requested. Below is the code for the algorithm I am trying to write.
inputFile is set to stdin if no file is specified
char buffer[kMaxLineSize + 1] = { '\0' };
char byte = 0;
int count = 1;
while((fread(buffer, 1, kMaxLineSize, inputFile)))
{
printf("%c", byte);
clearCRLF(buffer);
printf("Buffer: %s\n", buffer);
if(outputFormat == 1)
{
char s1Record[kMaxSRecordSize] = { 0 };
buildS1Record(addressField, s1Record, buffer);
fprintf(outputFile, "%s\n", s1Record);
addressField += strlen(buffer);
s1Count++;
}
else
{
char asmRecord[kMaxASMRecordSize] = { 0 };
buildAssemblyRecord(asmRecord, buffer);
fprintf(outputFile, "%s\n", asmRecord);
}
}
I'll try to combine the comments in this answer.
But first, what you call an s1 "record" is not a record. It is a string of maximum 16 characters and a terminating null character. A record, in my understanding, is a struct with fields of possibly different types, one of which could be a string.
The code fixes are as follows:
char buffer[kMaxLineSize + 1] = { '\0' };
int len;
while (len=fread(buffer, 1, kMaxLineSize, inputFile))
{
...
char s1Record[kMaxSRecordSize] = { 0 };
buildS1Record(addressField, s1Record, buffer, len);
so you pass the length read to the function. Now it can copy the characters read and terminate with a '\0' character. Note also that there can be a discrepancy between kMaxLineSize and kMaxSRecordSize: they must be the same size (plus 1 for \0), so better use a single variable.
I hope this late answer can still be of use to you.
I wrote a program that copy content from a file to another but when I used fread() to read data from a file and put into buffer it turn out it have more data than the text file
Here's my code
char *buffer;
int size;
FILE *fp1;
fp1 = fopen(src, "r");
if (fp1 == NULL) {
err = errno;
fprintf(stderr, "Value of errno: %d\n", errno);
fprintf(stderr, "Error opening file: %s\n", strerror( err ));
return 0;
}else{
fseek(fp1, 0, SEEK_END);
size = ftell(fp1);
buffer = (char *) malloc(size +1 );
printf("data in Buffer : %s\n",buffer);
printf("size : %d\n",size);
fseek(fp1, 0, SEEK_SET);
fread(buffer,size,1,fp1);
strcat(buffer,"\0");
printf("data in Buffer after fread(): %s\n",buffer);
int a = strlen(buffer);
printf("strlen in Buffer : %d\n",a);
fclose(fp1);
}
FILE *fp2;
fp2 = fopen("disk1.img", "a");
if (fp2 == NULL) {
err = errno;
fprintf(stderr, "Value of errno: %d\n", errno);
fprintf(stderr, "Error opening file: %s\n", strerror( err ));
}else{
rewind(fp2);
printf("data in Buffer before write to destination : %s\n",buffer);
fclose(fp2);
}
source file contain
test kub test ah hahaha 5
Result
data in Buffer : �
size : 26
data in Buffer after fread(): test kub test
ah hahaha 5
U*
strlen in Buffer : 30
data in Buffer before write to destination : test kub test
ah hahaha 5
U*
The file size is 26 bytes I specify 26 bytes in fread() but in turns out buffer contain 30 characters
I use fread() because I have to write data in specific position in destination file also I added "\0" after fread() because I though it could help but it didn't work
**This is second time I face this problem.First time I specific amount of byte when read data from buffer to solve this problem but now I want to know
Why buffer keep more data than the source file and How to fix it.
--------------------Update----------------------------
I read all comment then
I followed user2225104 suggestion and It worked !
I replaced strcat(buffer,"\0"); with buffer[size] = '\0';
Thank you all for your answer it makes me know c programming better.
Result
data in Buffer : 0u
size : 26
data in Buffer after fread(): test kub test
ah hahaha 5
strlen in Buffer : 26
data in Buffer before write to destination : test kub test
ah hahaha 5
The problem is your attempt to 0-terminate and turn the block of chars into a c-string.
strcat(buffer,"\0");
only works if the first string is already 0-terminated. If it were, you would not need it. As you say yourself, your supposed string length is larger than your buffer. So you read some random 0 value behind your buffers end and then overwrite memory 1 byte behind it with your strcat() operation.
buffer[size] = '\0';
This way to do it does not assume buffer is a 0-terminated string and will not hamper with memory outside buffer.
On a side note, malloc() can return NULL. Best make it a habit to ALWAYS check the results of heap operation functions, just as checking results on file operations (e.g. fopen()). Basically anything which can go wrong at run-time and is not an invariant should be checked.
There's two kinds of strings in the programming world:
the Pascal kind of string (used by managed languages like C# and Java), where the size of the string is stored as an integer separately
the C kind of strings, where the size is indicated by a terminating "special" character
There's pros and cons for each of them, but the most important thing is that C style strings can't hold binary data -- the terminating character chosen by C is a valid character in a file (obviously).
So instead you emulate Pascal strings and call them "buffers", basically vectors of characters of some kind, with the size stored manually. You can see it in your malloc call, and again in your fread. Then you sort of black out and forget you wrote it and stop using it, but the size is still there, it's not part of the string.
Instead of printing it with printf (which expects null terminated C strings), you should use a character buffer function like fwrite to write it, and give it the size as an argument. Instead you're printing memory past what you allocated (since it doesn't end with 0), buffer overruning yourself. Generally hackers don't need your help, if they put their mind to it, they'll do it themselves :)
As a side note, you don't need size+1 characters -- there's no terminator as explained.
It's because your code is invalid.
fread(buffer,size,1,fp1);
Here you are ignoring the count returned by fread(), which tells you how many bytes have just been read into the buffer.
strcat(buffer,"\0");
Here you are pointlessly appending a null character after the first null character in the buffer. Remove it.
printf("data in Buffer after fread(): %s\n",buffer);
Here again you are ignoring the count. Assuming you used int count = fread(...), this line should be
printf("data in Buffer after fread(): %.*s\n",count,buffer);
Then:
int a = strlen(buffer);
This line is pointless. You shouldn't assume that I/O operations result in null-terminated C strings. There's nothing anywhere that guarantees that. Instead, you should use the count again. So
printf("strlen in Buffer : %d\n",a);
should be
printf("byte count in Buffer : %d\n",count);
While I could use strings, I would like to understand why this small example I'm working on behaves in this way, and how can I fix it ?
int ReadInput() {
char buffer [5];
printf("Number: ");
fgets(buffer,5,stdin);
return atoi(buffer);
}
void RunClient() {
int number;
int i = 5;
while (i != 0) {
number = ReadInput();
printf("Number is: %d\n",number);
i--;
}
}
This should, in theory or at least in my head, let me read 5 numbers from input (albeit overwriting them).
However this is not the case, it reads 0, no matter what.
I understand printf puts a \0 null terminator ... but I still think I should be able to either read the first number, not just have it by default 0. And I don't understand why the rest of the numbers are OK (not all 0).
CLARIFICATION: I can only read 4/5 numbers, first is always 0.
EDIT:
I've tested and it seems that this was causing the problem:
main.cpp
scanf("%s",&cmd);
if (strcmp(cmd, "client") == 0 || strcmp(cmd, "Client") == 0)
RunClient();
somehow.
EDIT:
Here is the code if someone wishes to compile. I still don't know how to fix
http://pastebin.com/8t8j63vj
FINAL EDIT:
Could not get rid of the error. Decided to simply add #ReadInput
int ReadInput(BOOL check) {
...
if (check)
printf ("Number: ");
...
# RunClient()
void RunClient() {
...
ReadInput(FALSE); // a pseudo - buffer flush. Not really but I ignore
while (...) { // line with garbage data
number = ReadInput(TRUE);
...
}
And call it a day.
fgets reads the input as well as the newline character. So when you input a number, it's like: 123\n.
atoi doesn't report errors when the conversion fails.
Remove the newline character from the buffer:
buf[5];
size_t length = strlen(buffer);
buffer[length - 1]=0;
Then use strtol to convert the string into number which provides better error detection when the conversion fails.
char * fgets ( char * str, int num, FILE * stream );
Get string from stream.
Reads characters from stream and stores them as a C string into str until (num-1) characters have been read or either a newline or the end-of-file is reached, whichever happens first.
A newline character makes fgets stop reading, but it is considered a valid character by the function and included in the string copied to str. (This means that you carry \n)
A terminating null character is automatically appended after the characters copied to str.
Notice that fgets is quite different from gets: not only fgets accepts a stream argument, but also allows to specify the maximum size of str and includes in the string any ending newline character.
PD: Try to have a larger buffer.