So I am trying to do some very simple read/writes on a file. Since it's for an assignment I can't use more sophisticated functions using File*.
I can easily create a file and write to it but if I try to read back my content (it's the same content but my problem boils down to this) I don't get what I expect and I can't yet see why.
Here the code snippet that causes me problems:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char** argv){
int fdisk = open("testfile.txt", O_RDWR | O_CREAT | O_APPEND);
char buff [20] = "Just a short text!!!";
write(fdisk, buff, sizeof(buff));
char buff2[20];
read((fdisk), buff2, sizeof(buff2));
printf("Context of deleted file: %c\n",buff[1]);
printf("Context of deleted file: %c\n",buff2[1]);
return 0;
}
When you create a file you need to specify the file access mode:
int fdisk = open("testfile.txt", O_RDWR | O_CREAT | O_APPEND, 0666);
Otherwise the access mode is some indeterminate value.
And before reading it back you need to rewind it:
lseek(fdisk, 0, SEEK_SET); // rewind
The problem is that the write call leaves the file descriptor pointing to just after the data written (so more writes will go after that rather than overwriting the same data), so the following read call tries to read data after that which was written, and probably gets nothing.
I think it is a combination of issues:
You are not rewinding or re-opening the file, so when you read you are always reading from the end of the file.
You are using append mode, so it will add data to the end of the file. This means that after the first run you will be writing data at the end of the file but always reading from the beginning (assuming you address the first problem).
You are not setting the permissions, so you get random file permissions and the file may not be readable after creating it.
Your print statement is only printing the second character from each buffer, rather than a full string.
Here is a minimal working example. This compiles and runs with the expected results on my machine.
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char** argv){
int fdisk = open("testfile.txt", O_RDWR | O_CREAT, 0666);
char buff[] = "Just a short text!!!";
write(fdisk, buff, sizeof(buff));
lseek(fdisk, 0, SEEK_SET);
char buff2[sizeof(buff)];
read((fdisk), buff2, sizeof(buff2));
printf("Context of deleted file: %s\n",buff);
printf("Context of deleted file: %s\n",buff2);
return 0;
}
As a final note, the functions you are using (read/write) all return values indicating whether the operation was successful. You should check them. They would have indicated that the read operation in your problem was not actually reading any data (because it was at the end of the file).
Related
I have to write two separate programs, which one is a Producer and second is a Consumer (both running in separate terminals). I provide an argument to the Producer which can be a text or a single character. Then, producer creates a .txt file, puts single character into it then closes it. Consumer opens that file, reads that character and prints it on a terminal, then closes the file and deletes it.The whole process repeats itself. If provided argument includes *, for example * or text* it finishes the both programs, printing * before ending. I can only use functions: open(), close(), read(), write(), unlink(). The expected result looks like this:
I have written both codes, this is the Producer code:
(I am aware of the fact that i have unnecessarily defined SIZE and used it, please don't mind it)
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SIZE 1
int main(int argc, char *argv[]){
char buff;
do{
int fdi=-1;
while(fdi<0){
fdi=open("test.txt",O_WRONLY | O_CREAT | O_EXCL, 0666);
}
read(STDIN_FILENO,&buff,SIZE);
write(fdi,&buff,SIZE);
close(fdi);
}while(buff!='*');
return 0;
}
and this is the Consumer code:
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#define SIZE 1
int main(int argc, char *argv[]){
char buff;
do{
int fdi=-1;
while(fdi<0){
fdi=open("test.txt",O_RDONLY | O_EXCL);
}
int rdin=read(fdi,&buff,SIZE);
if(rdin>0){
write(STDOUT_FILENO,&buff,SIZE);
close(fdi);
unlink("test.txt");
}
else{
close(fdi);
}
}while(buff!='*');
return 0;
}
My question is: how does the Producer program not insert more than a one character into the file? I mean, if I for example run the Producer program only, and provide argument text, it will insert in the file the letter t only, the rest will be inserted to other files. Shouldn't it loop and add the whole text word to one file? There is no statement that guarantees that file will contain one character, yet it contains only one character and I don't know why.
In the producer, when you use the O_CREAT and O_EXCL flags when opening a file, the call will fail if the file already exists. So on the first iteration of the outer loop (assuming the file doesn't exist) the file is created and the first character is written. On the next iteration the open call fails because the file exists, so it sits in the inner loop for as long as the file exists.
In the consumer, the open call is done in a loop until it succeeds. This will happen when the producer writes and closes the file. The consumer then reads the character from the file and deletes it. When the consumer deletes the file, the open call in the producer will succeed and write the second character to the file.
This process then repeats until a * character is read by the producer and written to the file, after which the producer exits. Then when the consumer reads the * it also exits.
I will limit my answer to your only specific question:
how does the Producer program not insert more than a one character into the file?
You're doing the following in a loop:
do{
int fdi = -1;
while (fdi < 0){
// Open the file only if it does not exist, creating it.
fdi = open("test.txt", O_WRONLY | O_CREAT | O_EXCL, 0666);
}
// Read 1 char from stdin.
read(STDIN_FILENO, &buff, SIZE);
// Write that char to the beginning of the file.
write(fdi, &buff, SIZE);
// Close and truncate the file.
close(fdi);
} while(buff != '*');
The first time you open the file, it is also created. From the second time onwards, the combination of flags O_CREAT | O_EXCL will make open() fail with error EEXIST, since this combination of flags will open the file only if it doesn't already exist. After writing the first character, your program will run in an endless loop (while (fdi < 0)) trying to open the file a second time.
From the manual page for open():
O_EXCL: Ensure that this call creates the file: if this flag is specified in conjunction with O_CREAT, and pathname
already exists, then open() will fail.
So, first of all, you don't need the O_EXCL flag. Other than that, if you want to append data to the file instead of overwriting its content each time, you should add the O_APPEND flag when you open() the file. From the manual page:
O_APPEND: The file is opened in append mode. Before each write(2), the file offset is positioned at the end of the file,
as if with lseek(2). The modification of the file offset and the write operation are performed as a single
atomic step.
Yesterday in my class we started working with files for the first time. I wanted to see how it works, so I made a program where I write a word and that word has to be written in a certain file(that part worked). After that, I wanted to read from that file a number of characters and show it on the screen.
#include <stdio.h>
#include <stdlib.h>
#include <io.h>
#include <sys/stat.h>
#include <process.h>
#include <fcntl.h>
#include <string.h>
int main()
{
int df,m;
char c[50],d[50];
printf("c= \n");
gets(c);
m=strlen(c);
df=open("e:\\codeblocks\\fisperimente\\text2.txt",O_RDONLY|O_WRONLY);
if (df==-1) {printf("error");exit(1);}
write(df,c,m);
/*int i,n;
n=read(df,d,5);
for (i=1;i<=n;i++)
{
printf("%c",d[i]);
} */
close(df);
return 0;
}
What I put in my commentary is the part that doesn`t work. I noticed that if I printf n, it returns -1, which means that I did something wrong while reading from the file.
First, I would avoid gets() at all costs. It is unsafe, and is considered deprecated. It may be removed in the future. Instead, consider using fgets(), like so:
fgets(c, sizeof(c), stdin);
Next, your open is suspicious:
df=open("e:\\codeblocks\\fisperimente\\text2.txt",O_RDONLY|O_WRONLY);
Read only and write only are mutually exclusive. If you want to open a file for both read and write, you need O_RDWR.
And finally, after you write to your file, the file pointer points to the end of file. If you want to reread it, you need to seek back to the beginning. This will do that:
lseek(df, 0, SEEK_SET);
Once you do that, you can read your file back in.
You need to seek to the beginning of the file, to read it's contents back. Or you can just close it or open it again for reading.
Try use flag O_RDWR, the or'd result of read only and write only flags is not what you think it does ;)
Note that mode O_RDWR is not usually equal to O_RDONLY | O_WRONLY. Therefore, you probably opened the file with O_WRONLY (classically, O_RDONLY is 0). This is not yet your major problem; it will become one.
This question already has answers here:
Reading a text file backwards in C
(5 answers)
Closed 9 years ago.
I am supposed to create a program that takes a given file and creates a file with reversed txt. I wanted to know is there a way i can start the read() from the end of the file and copy it to the first byte in the created file if I dont know the exact size of the file?
Also i have googled this and came across many examples with fread, fopen, etc. However i cant use those for this project i can only use read, open, lseek, write, and close.
here is my code so far its not much but just for reference:
#include<stdio.h>
#include<unistd.h>
int main (int argc, char *argv[])
{
if(argc != 2)/*argc should be 2 for correct execution*/
{
printf("usage: %s filename",argv[0[]);}
}
else
{
int file1 = open(argv[1], O_RDWR);
if(file1 == -1){
printf("\nfailed to open file.");
return 1;
}
int reversefile = open(argv[2], O_RDWR | O_CREAT);
int size = lseek(argv[1], 0, SEEK_END);
char *file2[size+1];
int count=size;
int i = 0
while(read(file1, file2[count], 0) != 0)
{
file2[i]=*read(file1, file2[count], 0);
write(reversefile, file2[i], size+1);
count--;
i++;
lseek(argv[2], i, SEEK_SET);
}
I doubt that most filesystems are designed to support this operation effectively. Chances are, you'd have to read the whole file to get to the end. For the same reasons, most languages probably don't include any special feature for reading a file backwards.
Just come up with something. Try to read the whole file in memory. If it is too big, dump the beginning, reversed, into a temporary file and keep reading... In the end combine all temporary files into one. Also, you could probably do something smart with manual low-level manipulation of disk sectors, or at least with low-level programming directly against the file system. Looks like this is not what you are after, though.
Why don't you try fseek to navigate inside the file? This function is contained in stdio.h, just like fopen and fclose.
Another idea would be to implement a simple stack...
This has no error checking == really bad
get file size using stat
create a buffer with malloc
fread the file into the buffer
set a pointer to the end of the file
print each character going backwards thru the buffer.
If you get creative with google you can get several examples just like this.
IMO the assistance you are getting so far is not really even good hints.
This appears to be schoolwork, so beware of copying. Do some reading about the calls used here. stat (fstat) fread (read)
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
int main(int argc, char **argv)
{
struct stat st;
char *buf;
char *p;
FILE *in=fopen(argv[1],"r");
fstat(fileno(in), &st); // get file size in bytes
buf=malloc(st.st_size +2); // buffer for file
memset(buf, 0x0, st.st_size +2 );
fread(buf, st.st_size, 1, in); // fill the buffer
p=buf;
for(p+=st.st_size;p>=buf; p--) // print traversing backwards
printf("%c", *p);
fclose(in);
return 0;
}
I tried using the following code to read a text from the keyboard and write it into the file text.dat. The file was created but it was empty.
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <io.h>
#include <string.h>
int main()
{
char s[201];
int n,f = open("text.dat", O_RDWR | O_CREAT);
while (fgets(s,200,stdin) != NULL)
write(f, s,sizeof(s));
close(f);
return 0;
}
write(f, s, strlen(s))
Though I'd use read() instead of fgets() and used its result instead of strlen()
The write is wrong. Try this:
write(f, s, sizeof(s));
The second parameter should be a pointer to the beginning of s. What you're actually passing is a pointer to the pointer.
While you're at it, get rid of that unused int n:
int f = open("text.dat", O_RDWR | O_CREAT);
Edited to add
Your write() must use strlen() instead of sizeof() - you're probably writing out uninitialized junk, which makes it appear that the file is empty.
write(f, s, strlen(s));
You are opening file in wrong way. Try open("text.dat", O_RDWR | O_CREAT,0666 );
P.S.
You simply don't have write access to file.
here's another question about splice(). I'm hoping to use it to copy files, and am trying to use two splice calls joined by a pipe like the example on splice's Wikipedia page. I wrote a simple test case which only tries to read the first 32K bytes from one file and write them to another:
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main(int argc, char **argv) {
int pipefd[2];
int result;
FILE *in_file;
FILE *out_file;
result = pipe(pipefd);
in_file = fopen(argv[1], "rb");
out_file = fopen(argv[2], "wb");
result = splice(fileno(in_file), 0, pipefd[1], NULL, 32768, SPLICE_F_MORE | SPLICE_F_MOVE);
printf("%d\n", result);
result = splice(pipefd[0], NULL, fileno(out_file), 0, 32768, SPLICE_F_MORE | SPLICE_F_MOVE);
printf("%d\n", result);
if (result == -1)
printf("%d - %s\n", errno, strerror(errno));
close(pipefd[0]);
close(pipefd[1]);
fclose(in_file);
fclose(out_file);
return 0;
}
When I run this, the input file seems to be read properly, but the second splice call fails with EINVAL. Anybody know what I'm doing wrong here?
Thanks!
From the splice manpage:
EINVAL Target file system doesn't support splicing; target file is
opened in append mode; neither of the descriptors refers to a
pipe; or offset given for non-seekable device.
We know one of the descriptors is a pipe, and the file's not open in append mode. We also know no offset is given (0 is equivalent to NULL - did you mean to pass in a pointer to a zero offset?), so that's not the problem. Therefore, the filesystem you're using doesn't support splicing to files.
What kind of file system(s) are you copying to/from?
Your example runs on my system when both files are on ext3 but fails when I use an external drive (I forget offhand if it is DOS or NTFS). My guess is that one or both of your files are on a file system that splice does not support.
The splice(2) system call is for copying between files and pipes and not between files, so it can not be used to copy between files, as has been pointed out by the other answers.
As of Linux 4.5 however a new copy_file_range(2) system call is available that can copy between files. In the case of NFS it can even cause server side copying.
The linked man page contains a full example program.