Hey, so I haven't really got this properly yet, however I've managed to create a read file function which works. I did write some code but I can't seem to get it to write on different occasions without writing a file. E.g. I have a struct with employee info. I want to enter: id, timestamp, name, job role, yadayada. But if I use:
fPointer = open("file.txt", "w");
printf("Enter entry\n> ");
scanf(" %s", UserInput);
fscanf(fPointer, "CUSTOM ENTRY WHICH I WANT THE USER TO INPUT", UserInput);
This doesn't create a file or write to one. This is all I have. Any suggestions?
c++ - Writing into a text file without overwriting it - Stack Overflow
C function to insert text at particular location in file without over-writing the existing text - Stack Overflow
C write in the middle of a binary file without overwriting any existing content - Stack Overflow
How to output to a text file, without overwriting what was already on it?
Videos
Yes, it is possible. By opening it with r+ you open it for 'reading and writing' (while w opens it for writing freshly).
Regarding your other question: Open a file with w and write 1000 1024 byte blocks to the file like this:
FILE *fp = fopen("file", "wb");
if(fp) {
int i = 0;
char Buf[1024];
for(; i < 1000; ++i)
fwrite(Buf, 1, 1024, fp);
fclose(fp);
}
Just once more the fopen flags for you:
r -> Opens file for reading. (File remains unchanged)
w -> Opens file for writing. (File gets erased)
a -> Opens file for appending. (File remains unchanged, file pointer gets moved to end)
Aside from these three main types, you can add a few more additional options:
b -> Opens the file as binary, ignoring formatting characters like \n
t -> Opens the file as text, specifically parsing \n as \r\n under Windows.
Open the file, then use fseek to set your position to the beginning of your file.
Edit: use r+ mode when opening. From man fopen:
r+ Open for reading and writing. The stream is positioned at
the beginning of the file.
Open the stream in append-mode:
log.open("log.txt", fstream::app);
This will simply append the new output with the existing, giving you one big log file that grows over time.
One suggestion (if you're not doing it already) is to include some kind of timestamp in the logging data, so that when you're reading the file, you can correlate the logged data to a run of the program.
Use log.open("log.txt", fstream::app) for appending to file.
Read this reference for further info.
If you need a sophisticated mechanism for logging and timestamping, there's a useful SO post about logging frameworks for C++. Pantheios got the accepted answer.
While there are a couple of techniques to do it in-place, you're working with a text file and want to perform insertions. Operating systems typically don't support text file insertions as a file system primitive and there's no reason they should do that.
The best way to do that kind of thing is to open your file for reading, open a new file for writing, copy the part of the file before the insertion point, insert the data, copy the rest, and then move the new file over the old one.
This is a common technique and it has a purpose. If anything goes wrong (e.g. with your system), you still have the original file and can repeat the transaction later. If you start two instances of the process and use a specific pattern, the second instance is able to detect that the transaction has already been started. With exclusive file access, it can even detect whether the transaction was interrupted or is still running.
That way is much less error prone than any of the techniques performed directly on the original file and is used by all of those traditional tools like sed even if you ask them to work in-place (sed -i). Another bonus is that you can always rename the original file to one with a backup suffix before overwriting it (sed offers such an option as well).
The same technique is often used for configuration files even if your program is writing an entirely new version and doesn't use the original file for that. It hasn't been long since many internet magazines claimed that ext4 accidentally truncates configuration files to zero length. This was exactly because some applications kept the configuration files open and truncated while the system was forcedly shut down. Those application often tampered with the original configuration files before they had the data ready and then even kept them open without syncing them, which made the window for data corruption much larger.
TL;DR version:
When you value your data, don't destroy it before you have the replacement data ready.
No, there's no way to insert characters into an existing file. You will need to use a second file to do that.
Here's a function extend_file_and_insert() that does the job, more or less.
#include <sys/stat.h>
#include <unistd.h>
enum { BUFFERSIZE = 64 * 1024 };
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
/*
off_t is signed
ssize_t is signed
size_t is unsigned
off_t for lseek() offset and return
size_t for read()/write() length
ssize_t for read()/write() return
off_t for st_size
*/
static int extend_file_and_insert(int fd, off_t offset, char const *insert, size_t inslen)
{
char buffer[BUFFERSIZE];
struct stat sb;
int rc = -1;
if (fstat(fd, &sb) == 0)
{
if (sb.st_size > offset)
{
/* Move data after offset up by inslen bytes */
size_t bytes_to_move = sb.st_size - offset;
off_t read_end_offset = sb.st_size;
while (bytes_to_move != 0)
{
ssize_t bytes_this_time = MIN(BUFFERSIZE, bytes_to_move);
ssize_t rd_off = read_end_offset - bytes_this_time;
ssize_t wr_off = rd_off + inslen;
lseek(fd, rd_off, SEEK_SET);
if (read(fd, buffer, bytes_this_time) != bytes_this_time)
return -1;
lseek(fd, wr_off, SEEK_SET);
if (write(fd, buffer, bytes_this_time) != bytes_this_time)
return -1;
bytes_to_move -= bytes_this_time;
read_end_offset -= bytes_this_time; /* Added 2013-07-19 */
}
}
lseek(fd, offset, SEEK_SET);
write(fd, insert, inslen);
rc = 0;
}
return rc;
}
(Note the additional line added 2013-07-19; it was a bug that only shows when the buffer size is smaller than the amount of data to be copied up the file. Thanks to malat for pointing out the error. Code now tested with BUFFERSIZE = 4.)
This is some small-scale test code:
#include <fcntl.h>
#include <string.h>
static const char base_data[] = "12345";
typedef struct Data
{
off_t posn;
const char *data;
} Data;
static const Data insert[] =
{
{ 2, "456" },
{ 4, "XxxxxxX" },
{ 12, "ZzzzzzzzzzzzzzzzzzzzzzzzX" },
{ 22, "YyyyyyyyyyyyyyyY" },
};
enum { NUM_INSERT = sizeof(insert) / sizeof(insert[0]) };
int main(void)
{
int fd = open("test.dat", O_RDWR | O_TRUNC | O_CREAT, 0644);
if (fd > 0)
{
ssize_t base_len = sizeof(base_data) - 1;
if (write(fd, base_data, base_len) == base_len)
{
for (int i = 0; i < NUM_INSERT; i++)
{
off_t length = strlen(insert[i].data);
if (extend_file_and_insert(fd, insert[i].posn, insert[i].data, length) != 0)
break;
lseek(fd, 0, SEEK_SET);
char buffer[BUFFERSIZE];
ssize_t nbytes;
while ((nbytes = read(fd, buffer, sizeof(buffer))) > 0)
write(1, buffer, nbytes);
write(1, "\n", 1);
}
}
close(fd);
}
return(0);
}
It produces the output:
12456345
1245XxxxxxX6345
1245XxxxxxX6ZzzzzzzzzzzzzzzzzzzzzzzzZ345
1245XxxxxxX6ZzzzzzzzzzYyyyyyyyyyyyyyyYzzzzzzzzzzzzzzZ345
It should be tested on some larger files (ones bigger than BUFFERSIZE, but it would be sensible to test with a BUFFERSIZE a lot smaller than 64 KiB; I used 32 bytes and it seemed to be OK). I've only eyeballed the results but the patterns are designed to make it easy to see that they are correct. The code does not check any of the lseek() calls; that's a minor risk.
First, use ftruncate() to enlarge the file to the final size. Then copy everything from the old end over to the new end, working your way back to the insertion point. Then overwrite the middle contents with the data you want to insert. This is as efficient as it gets, I think, because filesystems don't generally offer true "insertion" in the middle of files.
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ofstream myText;
int i = 0, arr[3] = { 1, 2, 3 };
myText.open("text.txt");
while (i < 3)
{
myText << arr[i];
i++;
}
myText.close();
return 0;
}(Simplified code)
When I run the program, it outputs '123', but when I run it again, instead of there being '123123', it is just '123'. How do I fix this?
I'm currently tinkering around with a project and I'm having bit of an issue with my write command.
Right now I have the following:
for (int i = 1; i < count; i++) {
char* fileByYear[5];
sprintf(fileByYear, "%d.txt", movieArray[i].year);
char* buffer[50];
printf("\nCalling sprintf on string creation -- ");
sprintf(buffer, "%s\n", movieArray[i].title);
printf("%s\n", buffer);
int fd = open(fileByYear, O_RDWR | O_CREAT | O_APPEND | O_TRUNC, 0640);
write(fd, buffer, strlen(buffer +1));
close(fd);
}My printf on the buffer confirms the string is being corrected correctly with sprintf()
Calling sprintf on string creation -- The Incredible Hulk Calling sprintf on string creation -- Sherlock Holmes Calling sprintf on string creation -- Iron Man
But with what is being written the file files is:
2008.txt >> I
2009.txt >> sherlock
It seems the whole string isn't being written correctly. I believe it maybe an issue with my permissions. The permissions are set to:
The permissions on these files must be set to
rw-r-----
-
i.e., the owner can read and write to the file, while group can only read the file.
The possible issue is the files aren't being saved into the directory that is being created, but I'm still trying to figure that out
You decide that in fopen. Just use "w" or "w+" instead of "r+".
As far as I can tell, you can't overwrite anything with fprintf. You have to use fwrite, e.g. something like
Copyrewind(file);
char buf[10];
sprintf(buf, "%d", 2);
fwrite(buf, strlen(buf), 1, file);
From your code and question I don't quite follow what it is you actually try to do, but hope this helps (half a year after you asked).
Hello! For some context I am making a linux terminal tool that allows you to interact with your files. I have every other feature done but I was wondering if you guys had any resources or knew of a good way to go about this.