C program reading and writing to binary files - c

Hello this is the first time I am working with binary file in C.
I am trying to first write the data to the file and then I to read the same data from the file. But somehow I am not reading the data correctly (that's what I think) because I think the file writing part has no issue. And I am doing all of this in unix.
So basically I am trying to write and read data from the binary file in unix and I am not able to read the data properly. Any help would be appreciated. Thanks in advance.
I have attached the code and the output below so it is understandable:
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
char data[100];
if(argc<2){
printf("Usage: gift <textstring>\n");
return -1;
}
int n = 0;
int num = 0;
char data[100];
if(argc<2){
printf("Usage: gift <textstring>\n");
return -1;
}
int n = 0;
int num = 0;
int fd = open("gifts.dat", O_CREAT | O_WRONLY | O_RDONLY);
if((strcmp(argv[1],"new"))==0){
for(int i = 2; i < argc; i = i+2){
int number = atoi(argv[i+1]);
strcpy(data, argv[i]);
int length = strlen(data) + 1;
write(fd,&length, sizeof(int));
write(fd,data, length);
write(fd,&number, sizeof(int));
}
}
else{
printf("Not worked");
}
for(int k =0;k < ((argc-2)/2);k++){
int length;
char*name = NULL;
int money;
read(fd, &length, sizeof(int));
name = malloc(length);
read(fd, name, length);
read(fd, &money, sizeof(int));
printf("%10s: %.2f\n", name, money);
}
return 0;
}
And then here is the output of the binary file
gcc -o gifts board.c
./gifts new patel 200 Ram 500
: 0.00
: 0.00
od -c gifts.dat
0000000 006 \0 \0 \0 p a t e l \0 310 \0 \0 \0 004 \0
0000020 \0 \0 R a m \0 364 001 \0 \0 \0 \0
0000034
cat gifts.dat
patelÈRamô{cslinux1:~/CS3377_hmb180006}
Make sure to use alias gcc='gcc -std=c99'before compiling with gcc

The primary problem is with your open() call. You are attempting to open the file as both read-only and write-only at the same time. From man 2 open:
The argument flags must include one of the following access
modes: O_RDONLY, O_WRONLY, or O_RDWR.
Refactoring your code as follows works fine.
char data[100];
int fd = open ("gifts.dat", O_CREAT | O_WRONLY);
int n = 0;
if ((strcmp(argv[1],"new")) == 0) {
for (int i = 2; i < argc; i = i+2){
int number = atoi (argv[i+1]);
strcpy (data, argv[i]);
int length = strlen(data) + 1;
write (fd, &length, sizeof(int));
write (fd, data, length);
write (fd, &number, sizeof(int));
n++;
}
}
else{
printf("Not worked");
}
close (fd);
fd = open ("gifts.dat", O_CREAT | O_RDONLY);
for (int k = 0; k < n; k++){
int length;
char name[100];
int money;
read (fd, &length, sizeof(int));
read (fd, name, length);
read (fd, &money, sizeof(int));
printf("%10s: %.2d\n", name, money);
}
close (fd);
Example Use/Output
$ ./bin/readwrite_bin new patel 200 Ram 500
patel: 200
Ram: 500
Additionally, you must validate all opens, reads, writes (and close - after write) by checking the return of each call to ensure is succeeds, or handle any error if it fails. You should replace atoi() with at minimum sscanf() or better strtol() as atoi() provides zero error detection and will happily accept atoi ("my cow");
Let me know if you have further questions.

Related

Reading a Binary File for Floats and Putting them into an Array

I am reading a binary file for floats and storing them in an array. There is a set of 4 byte floating-point values in the file. However, with my current code, the values of the last two indexes of the array are always read as the same value instead of what the actual value of the last index is.
For example, I am getting
array[0] = -123456.123456
array[1] = 123456.123456
array[2] = 123456.123456
when I should be getting something like
array[0] = -123456.123456
array[1] = 123456.123456
array[2] = 654321.654321
I am not sure what I am doing incorrectly in terms of the reading and why I am getting this output.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <float.h>
int main(int argc, char *argv[]) {
int fd;
float num;
size_t nr;
int elementnum = 0;
fd = open(argv[1], O_RDONLY);
nr = read(fd, &num, sizeof(float));
if (fd == -1){
printf("Error opening file");
exit(1);
}
if (nr == -1){
printf("Error reading file");
exit(1);
}
struct stat st;
fstat(fd, &st);
off_t size = st.st_size;
for (int j = 0; j < size/4; j++){
elementnum++;
}
printf("number of elements: %d\n", elementnum);
float array[elementnum];
for (int i = 0; i < elementnum; i++){
nr = read(fd, &num, sizeof(float));
array[i] = num;
}
for (int i = 0; i < elementnum; i++){
printf("Before Sorting: %f\n", array[i]);
}
close(fd);
return 0;
}
You need to check the value returned by each read. Since you read one byte out of the file right after you open it (apparently just to validate that one read works) and do not rewind, your loop is trying to read one more value than is available. If you check the read on the final iteration, I suspect you will see that it returns zero. Since it returns 0, it does not change the value of num, so num retains the value it had on the penultimate iteration.
IOW, stop reading a byte after you open. Instead, just open the file and check that open was successful. Then read until read returns 0 or -1. If read returns 0 before you expect it or if it returns -1, then print an error message.

Weird output from integer array to a file

Hi I am writing a program that generates random ints, put them in an array and save them to a file. Everything seems to work good but after I open this file it has this strange content : ^K^#^#^S^#^#^#^[^#
What have I done wrong?
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char *argv[]) {
int tab[10];
int fd;
srand(time(0));
int i;
for(i = 0; i < 10; i++)
tab[i] = rand() % 50;
if(argc != 2 || strcmp(argv[1], "--help") == 0)
{
.......
}
fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0644);
write(fd, tab, 10);
close(fd);
return 0;
}
The content is wierd because you're writing binary values, random character codes from 0 to 50. But the information is there all right (well, you have to write sizeof(int) times more data to store all the data though, and it can be corrupt on Window because you're missing O_BINARY and some carriage return chars may be inserted at some locations...):
fd = open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0644); // add | O_BINARY if you use windows
write(fd, tab, 10 * sizeof(int)); // you can use (fd,tab,sizeof(tab)) too as it's an array, not a pointer
Use a hex editor you'll see the values (with a lot of zeroes since your values can be encoded in a byte). But not with a text editor.
If you want to write formatted integers as strings, use fopen and fprintf on the values, in a text file, not binary. Quick & dirty (and also untested :)):
FILE *f = fopen(argv[1], "w"); // #include <stdio.h> for this
if (f != NULL)
{
int i;
for (i = 0; i < 10; i++)
{
fprintf(f,"%d ",tab[i]);
}
fclose(f);
}

mmap is wiping my file instead of copying it

So I'm using mmap to then write to another file. But the weird thing is, when my code hits mmap, what it does is clears the file. So I have a file that's populated with random characters (AB, HAA, JAK, etc...). What it's supposed to do is use mmap as read basically and then write that file to the new file. So that first if (argc == 3) is the normal read and write, the second if (argc ==4) is supposed to use mmap. Does anyone have any idea why on Earth this is happening?
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/io.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/resource.h>
int main(int argc, char const *argv[])
{
int nbyte = 512;
char buffer[nbyte];
unsigned char *f;
int bytesRead = 0;
int size;
int totalBuffer;
struct stat s;
const char * file_name = argv[1];
int fd = open (argv[1], O_RDONLY);
int i = 0;
char c;
int fileInput = open(argv[1], O_RDONLY);
int fileOutPut = open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
fstat(fileInput, &s);
size = s.st_size;
printf("%d\n", size);
if (argc == 3)
{
printf("size: %d\n", size);
printf("nbyte: %d\n", nbyte);
while (size - bytesRead >= nbyte)
{
read(fileInput, buffer, nbyte);
bytesRead += nbyte;
write(fileOutPut, buffer, nbyte);
}
read(fileInput, buffer, size - bytesRead);
write(fileOutPut, buffer, size - bytesRead);
}
else if (argc == 4)
{
int i = 0;
printf("4 arg\n");
f = (char *) mmap (0, size, PROT_READ, MAP_PRIVATE, fileInput, 0);
/* This is where it is being wipped */
}
close(fileInput);
close(fileOutPut);
int who = RUSAGE_SELF;
struct rusage usage;
int ret;
/* Get the status of the file and print some. Easy to do what "ls" does with fstat system call... */
int status = fstat (fd, & s);
printf("File Size: %d bytes\n",s.st_size);
printf("Number of Links: %d\n",s.st_nlink);
return 0;
}
EDIT: I wanted to mention that the first read and write works perfectly, it is only when you try to do it through the mmap.
If you mean it's clearing your destination file, then yes, that's exactly what your code will do.
It opens the destination with truncation and then, in your argc==4 section, you map the input file but do absolutely nothing to transfer the data to the output file.
You'll need a while loop of some description, similar to the one in the argc==3 case, but which writes the bytes in mapped memory to the fileOutput descriptor.

Problems with FILE Unbuffered I/O: Read and Write a String in C

I'm a student of computer science, to practice with Unbuffered I/O function, I tried to write some simple code in C. This code creates a file and then tries to write in this file one or more strings that we pass from the terminal. The output is not as expected. These are the commands that I write on the terminal.
gcc file_IO.c
./a.out file.txt hello world
cat file.txt
hello��*world2��
In "file.txt" there is only the string "hello". How can I print the string written to the file?
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
#define BUFFER_SIZE 1024
int main(int argc, char *argv[])
{
mode_t access_mode = S_IRUSR | S_IWUSR;
int flags = O_RDWR | O_CREAT | O_TRUNC;
int fd, i,n_char,index = 0;
char buffer[BUFFER_SIZE];
char buff_out[BUFFER_SIZE];
if( (fd = open(argv[1], flags, access_mode)) == -1 ){
perror("open");
exit(EXIT_FAILURE);
}
for(i = 2; i < argc; i++){
n_char = sprintf(buffer+index,"%s",argv[i]);
write(fd,buffer+index,sizeof(argv[i]) *4);
read(fd,buff_out,sizeof(argv[i]) * 4);
index += (strlen(argv[i]) +1);
}
if(( close(fd)) == -1 ){
perror("close");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
Thank you for all your assistance.
You are using sizeof to get the length of a string. That returns the size of a pointer (usually 4) not the length of the string. strlen should be used instead.
You are not checking the result of write and read. They can fail or can read/write only part of what you asked.
sprintf and read are not necessary to write the string to a file. Try:
for(i = 2; i < argc; i++){
write(fd, argv[i], strlen(argv[i]));
}
Notice that this still doesn't check return value from write.
You can't use sizeof to determine the length of a runtime string.
You also don't need to use snprintf at all. The string in argv can be written directly to the output file.
Try this instead:
write(fd,argv[i],strlen(argv[i]));
Also be aware that this won't output any spaces or newlines between the words.

How does read(2) interact with carriage returns?

I am writing a simple program to flip all the bits in a file, but right now it only does the first 1000 bytes until I get that much working. Why does my call to read() ignore \r characters? When I run this code on a file that only contains \r\n\r\n, the read call returns 2 and the buffer contains \n\n. The \r characters are completely ignored. I'm running this on Windows (this wouldn't even be an issue on Linux machines)
Why does read(2) skip over the \r character when it finds it? Or is that what is happening?
EDIT: Conclusion is that windows defaults to opening files in "text" mode as opposed to "binary" mode. For this reason, when calling open, we must specify O_BINARY as the mode.
Thanks, code below.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
void invertBytes(size_t amount, char* buffer);
int main(int argv, char** argc)
{
int fileCount = 1;
char* fileName;
int fd = 0;
size_t bufSize = 1000;
size_t amountRead = 0;
char* text;
int offset = 0;
if(argv <= 1)
{
printf("Usages: encode [filenames...]\n");
return 0;
}
text = (char *)malloc(sizeof(char) * bufSize);
for(fileCount = 1; fileCount < argv; fileCount++)
{
fileName = argc[fileCount];
fd = open(fileName, O_RDWR);
printf("fd: %d\n", fd);
amountRead = read(fd, (void *)text, bufSize);
printf("Amount read: %d\n", amountRead);
invertBytes(amountRead, text);
offset = (int)lseek(fd, 0, SEEK_SET);
printf("Lseek to %d\n", offset);
offset = write(fd, text, amountRead);
printf("write returned %d\n", offset);
close(fd);
}
return 0;
}
void invertBytes(size_t amount, char* buffer)
{
int byteCount = 0;
printf("amount: %d\n", amount);
for(byteCount = 0; byteCount < amount; byteCount++)
{
printf("%x, ", buffer[byteCount]);
buffer[byteCount] = ~buffer[byteCount];
printf("%x\r\n", buffer[byteCount]);
}
printf("byteCount: %d\n", byteCount);
}
fd = open(fileName, O_RDWR);
should be
fd = open(fileName, O_RDWR | O_BINARY);
See read() only reads a few bytes from file for details.
Try opening with O_BINARY to use binary mode, text mode may be default and may ignore \r.
open(fileName, O_RDWR|O_BINARY);

Resources