I want to read and write a text or .dat file in verifone to store data on it.
How can I make it ?
here is my code
int main()
{
char buf [255];
FILE *tst;
int dsply = open(DEV_CONSOLE , 0);
tst = fopen("test.txt","r+");
fputs("this text should write in file.",tst);
fgets(buf,30,tst);
write(dsply,buf,strlen(buf));
return 0;
}
Chapter 3 of the "Programmers Manual for Vx Solutions" ("23230_Verix_V_Operating_System_Programmers_Manual.pdf") is all about file management and contains all the functions I typically use when dealing with data files on the terminal. Go read through that and I think you'll find everything you need.
To get you started, you'll want to use open() together with the flags you want
O_RDONLY (read only)
O_WRONLY (write only)
O_RDWR (read and write)
O_APPEND (Opens with the file position pointer at the end of the file)
O_CREAT (create the file if it doesn't already exist),
O_TRUNC (truncate/delete previous contents if the file already exists),
O_EXCL (Returns error value if the file already exists)
On success, open will return a positive integer that is a handle which can be used for subsequent access to the file. On failure, it returns -1;
When the file is open, you can use read() and write() to manipulate the contents.
Be sure to call close() and pass in the return value from open when you are done with the file.
Your example above would look something like this:
int main()
{
char buf [255];
int tst;
int dsply = open(DEV_CONSOLE , 0);
//next we will open the file. We will want to read and write, so we use
// O_RDWR. If the files does not already exist, we want to create it, so
// we use O_CREAT. If the file *DOES* already exist, we want to truncate
// and start fresh, so we delete all previous contents with O_TRUNC
tst = open("test.txt", O_RDWR | O_CREAT | O_TRUNC);
// always check the return value.
if(tst < 0)
{
write(dsply, "ERROR!", 6);
return 0;
}
strcpy(buf, "this text should write in file.")
write(tst, buf, strlen(buf));
memset(buf, 0, sizeof(buf));
read(tst, buf, 30);
//be sure to close when you are done
close(tst);
write(dsply,buf,strlen(buf));
//you'll want to close your devices, as well
close(dsply);
return 0;
}
Your comments also ask about searching. For that, you'll also need to use lseek with one of the following which specifies where you are starting from:
SEEK_SET — Beginning of file
SEEK_CUR — Current seek pointer location
SEEK_END — End of file
example
SomeDataStruct myData;
...
//assume "curPosition" is set to the beginning of the next data structure I want to read
lseek(file, curPosition, SEEK_SET);
result = read(file, (char*)&myData, sizeof(SomeDataStruct));
curPosition += sizeof(SomeDataStruct);
//now "curPosition" is ready to pull out the next data structure.
NOTE that the internal file pointer is already AT "curPosition", but doing it this way allows me to move forward and backward at will as I manipulate what is there. So, for example, if I wanted to move back to the previous data structure, I would simply set "curPosition" as follows:
curPosition -= 2 * sizeof(SomeDataStruct);
If I didn't want to keep track of "curPosition", I could also do the following which would also move the internal file pointer to the correct place:
lseek(file, - (2 * sizeof(SomeDataStruct)), SEEK_CUR);
You get to pick whichever method works best for you.
Related
I want to store student information in a file in C, I choose to write in a binary file format.first I had written all student data to the file in write mode. first 4 bytes(int) represents the num of students and then subsequent data is for storing student Information.
when I want to append some more student data to the file, I can apped the student data at the end of file in append mode but could not update the no of students at the start of file (first 4 bytes(int)) which are already written in write mode. There is no where stated in manpages that I can't rewrite the data that are already written in write mode. Below is the code. Thanks in advance.
typedef struct
{
char* rollNo;
char* name;
char* grade;
}Student;
int numStudents;
void append(Student* newStudent){
FILE *fp;
if((fp=fopen("students.bin","ab"))==NULL){ f_err_report(ERR_OPEN);exit(1);}
//appending the new student at the end of the file
// writing rollNo
int len;
len = strlen(newStudent->rollNo)+1;
if(fwrite(&len,sizeof(int),1,fp)!=1) {
f_err_report(ERR_WRITE);
}
if(fwrite(newStudent->rollNo,sizeof(char)*len,1,fp)!=1) {
f_err_report(ERR_WRITE);
}
//writing name
len = strlen(newStudent->name)+1;
if(fwrite(&len,sizeof(int),1,fp)!=1) {
f_err_report(ERR_WRITE);
}
if(fwrite(newStudent->name,sizeof(char)*len,1,fp)!=1) {
f_err_report(ERR_WRITE);
}
//writing grade
len = strlen(newStudent]->grade)+1;
if(fwrite(&len,sizeof(int),1,fp)!=1) {
f_err_report(ERR_WRITE);
}
if(fwrite(newStudent->grade,sizeof(char)*len,1,fp)!=1) {
f_err_report(ERR_WRITE);
}
//updating the total number of students at the file starting. But this doesn't happen!!! It stays same as when I wrote in "write mode" previously
numStudents++;
rewind(fp);
if(fwrite(&numStudents,sizeof(int),1,fp)!=1){
f_err_report(ERR_WRITE);
}
printf("appended to the list \n");
fclose(fp);
}
When you called
rewind(fp);
you successfully reset the file pointer to the beginning of the file, ready to update the number of student records. All right so far.
But then you called
fwrite(&numStudents, sizeof(int), 1, fp)
to actually update the number of records. And since the file is opened in append mode, this translates implicitly to the equivalent of
fseek(fp, OL, SEEK_END);
fwrite(&numStudents, sizeof(int), 1, fp)
That is, in append mode, every time you write to the file, there's an automatic seek to the end of the file, followed by the write you asked for.
So your rewind() call accomplishes nothing in the long run, because it's undone by the next write.
Once upon a time, calling fopen with "a" mode basically just opened the file, and seeked to the end once. In those days, your code would have worked as you expected. But not any more. In your case, you want to use "w" mode (or "w+" or "r+", if you want to both read and write), and when you want to append a new record at the end, call
fseek(fp, OL, SEEK_END);
to explicitly get to the end when you need to.
when I want to append some more student data to the file, I can apped the student data at the end of file in append mode but could not update the no of students at the start of file (first 4 bytes(int)) which are already written in write mode. There is no where stated in manpages that I can't rewrite the data that are already written in write mode.
Yes there is:
Opening a file with append mode ('a' as the first character in the mode argument) causes all subsequent writes to the file to be forced to the then current end-of-file, regardless of intervening calls to the fseek function.
And from the Linux man page:
Opening a file in append mode (a as the first character of mode)
causes all subsequent write operations to this stream to occur at
end-of-file, as if preceded the call:
fseek(stream, 0, SEEK_END);
The only way on POSIX systems to open a file in append mode but still be able to write to any position in a file is to use the POSIX open() call with the O_APPEND flag and then use the POSIX pwrite() call to write to the desired location:
The pwrite() function shall be equivalent to write(), except that it writes into a given position and does not change the file offset (regardless of whether O_APPEND is set).
But pwrite() is broken on Linux:
POSIX requires that opening a file with the O_APPEND flag should
have no effect on the location at which pwrite() writes data.
However, on Linux, if a file is opened with O_APPEND, pwrite()
appends data to the end of the file, regardless of the value of
offset.
I am reading a file in a while loop from start to end:
FILE *file;
file = fopen(path_to_file), "r");
char *line = NULL;
size_t len = 0;
while (getline(&line, &len, file) > 0) {
delete_line_from_file(line);
}
fclose(file);
The function delete_line_from_file() removes the line passed to it from the file. It reads in the whole file via open(fd, O_RDONLY | O_CLOEXEC) + read() + close(), then removes the line from the buffer and writes the whole buffer to the same file via open(fd, O_WRONLY | O_TRUNC | O_CLOEXEC) + write() + close(). The read() is locked in an advisory read-lock via struct flock lk and the write() is locked in an advisory write-lock.
When I read the file there are lines that get missed which has something to do with me reading the file from start to finish in one loop while writing to it. If I read in the whole file and go through the buffer line-by-line no lines get missed. (This is my preferred solution so far.) There are also no mistakes made when truncating and writing the file. The missed lines are still in the file after the loop finishes.
Can I make sure that my while-loop does not miss a line and cleanly empties the file? The file needs to be emptied line-by-line. It cannot be just truncated.
Here is one possible solution I had in mind. Mirror the file via fstat(file &fbuf) and check it's size with if (fbuf.st_size !=0) fseek(file, 0, SEEK_SET); but that seems inefficient.
So is the goal to empty the file completely?
Why don't you open the file as such:
open("file", O_TRUNC | O_WRONLY);
This will open the file with truncation. Alternatively, and perhaps a better solution, you can do this:
fopen("file", "w");
fopen with the "w" option delete the original file and replaces it with the new file of name "file".
Use fseek and ftell inside your loop.
Two processes modifying the same file is a recipe for problems. May be you need to use a pipe(2).
I am new to programming in C and I am programming for the Raspberry Pi using a C compiler. All I want to be able to do is create a function that takes a String as a parameter and save it as a text file in a specific location. I want to check that file location to see what files exist and save the new file to that folder with an increment of 1 added to the file name.
For example, folder contains:
TestFile1
TestFile2
And I want to be able to create the new file saved as TestFile3.
This is the code that I have so far and want to know if I am on the right lines and get any tips please:
void WriteToFile(unsigned char *pID)
{
printf("Writing to file. . . . .\n");
/* Checking to see how many files are in the directory. */
int *count = 0;
DIR *d;
struct dirent *dir;
d = opendir("table_orders");
if(d)
{
while((dir = readdir(d)) != NULL)
{
printf("%s\n", dir->d_name);
count = count + 1; // Adds 1 to count whenever a file is found.
}
closedir(d);
}
char str[sizeOf(count)]; // Creates string.
sprintf(str, "%d", count); // Formats count integer into a string.
File *f = fopen("table_orders/Order " + str + ".txt", "a"); // Creates new file.
if(f == NULL)
{
printf("Error opening file!\n");
exit(1);
}
fprintf(f, "Order: %s \n", pID);
fclose(f);
printf("The Order %s has been written to the file\n", pID);
}
int fd = open( "filename", O_RDWR | O_CREAT | O_EXCL, 0644 );
Nothing else is atomic - another process can create the file in between any check for existence and your actual creation of the file.
You can use stat (_stat in Windows) to see if a file exits. If it fails with errno set to ENOENT then the file doesn't exist. access is another possibility.
Of course it's not atomic, some other process could create the file in between your check and your call to fopen.
You are close, but so far off the mark, I think you need to put this on hold. Read some c tutorials with structured examples.
You have an algorithm error when you read through the directory and arbitrarily increase count. The logic should be more like
parse dir->name to 3 tokens "Testfile" 0001 ".log"
Using atoi or similar convert the numeral string to an int
When you declare str there's a lack of understanding of sizeof, it should read more like
char str[25];
This is enough to hold all digits of a 4 byte int as a string. sizeof count will be 4 probably,4 bytes 32 bits.
When you fopen you do something like "Dir/file" + str + ".log"
This isn't how you do this, + is mathematical in c. You need to use strcat or sprintf into a new work string, created and freed on the fly.
When appending a number to a file like here it makes sense to precede short numbers with 0 s . This produces regular filenames which have a good ls order.
Should you need to control create and exclusivity, you'll need to use open to open the file and fdopen it to a FILE type file handle.
When you go live with this you will need to prevent another process causing timing errors with scheduling use semaphores or a ".lock" file.
You will also want some maintenance routine to delete old logs and subsequently renumber all the remaining logs from 001
PS don't know why the down votes
I'm having a problem getting the correct file position at which I'm writing when simultaneously writing to different parts of the same file using multiple threads.
I have one global file descriptor to the file. In my writing function, I
first lock a mutex, then do lseek(global_fd, 0, SEEK_CUR) to get the current file
position. I next write 31 zero bytes (31 is my entry size) using write(), in effect to reserve space for later. I then unlock the mutex.
Later in the function, I declare a local fd variable to the same file, and open
it. I now do an lseek on that local fd to get to the position I learned from
earlier, where my space is reserved. Finally, I write() 31 data bytes there for
the entry, and close the local fd.
The issue seems to be that rarely, an entry doesn't get written to the expected location (it's not mangled data - it seems that either it is swapped with a different entry, or two entries were written to the same location). There are multiple threads running that
"writing function" I described.
I since learned that pwrite() can be used to write to a specific offset, which would be more efficient, and eliminate the lseek(). However, I first want to find out: what is wrong with my original algorithm? Is there any type of buffering that could be causing the discrepancy between the expected write location, and where the data actually ends up getting stored in the file?
The relevant code snippet is below. The reason this is an issue is that in a second data file, I record the location where the entry I'm writing will be stored. If that location, based on the lseek() before the write, is not accurate, my data doesn't match up properly -- which is what happens on occasion (it's hard to reproduce - it happens in maybe 1 in 100k writes). Thanks!
db_entry_add(...)
{
char dbrecord[DB_ENTRY_SIZE];
int retval;
pthread_mutex_lock(&db_mutex);
/* determine the EOF index, at which we will add the log entry */
off_t ndb_offset = lseek(cfg.curr_fd, 0, SEEK_CUR);
if (ndb_offset == -1)
{
fprintf(stderr, "Unable to determine ndb offset: %s\n", strerror_s(errno, ebuf, sizeof(ebuf)));
pthread_mutex_unlock(&db_mutex);
return 0;
}
/* reserve entry-size bytes at the location, at which we will
later add the log entry */
memset(dbrecord, 0, sizeof(dbrecord));
/* note: db_write() is a write() loop */
if (db_write(cfg.curr_fd, (char *) &dbrecord, DB_ENTRY_SIZE) < 0)
{
fprintf(stderr, "db_entry_add2db - db_write failed!");
close(curr_fd);
pthread_mutex_unlock(&db_mutex);
return 0;
}
pthread_mutex_unlock(&db_mutex);
/* in another data file, we now record that the entry we're going to write
will be at the specified location. if it's not (which is the problem,
on rare occasion), our data will be inconsistent */
advertise_entry_location(ndb_offset);
...
/* open the data file */
int write_fd = open(path, O_CREAT|O_LARGEFILE|O_WRONLY, 0644);
if (write_fd < 0)
{
fprintf(stderr, "%s: Unable to open file %s: %s\n", __func__, cfg.curr_silo_db_path, strerror_s(errno, ebuf, sizeof(ebuf)));
return 0;
}
pthread_mutex_lock(&db_mutex);
/* seek to our reserved write location */
if (lseek(write_fd, ndb_offset, SEEK_SET) == -1)
{
fprintf(stderr, "%s: lseek failed: %s\n", __func__, strerror_s(errno, ebuf, sizeof(ebuf)));
close(write_fd);
return 0;
}
pthread_mutex_unlock(&db_mutex);
/* write the entry */
/* note: db_write_with_mutex is a write() loop wrapped with db_mutex lock and unlock */
if (db_write_with_mutex(write_fd, (char *) &dbrecord, DB_ENTRY_SIZE) < 0)
{
fprintf(stderr, "db_entry_add2db - db_write failed!");
close(write_fd);
return 0;
}
/* close the data file */
close(write_fd);
return 1;
}
One more note, for completeness. I have a similar but simpler routine that could also be causing the problem. This one uses buffered output (FILE*, fopen, fwrite), but performs an fflush() at the end of each write. It writes to a different file than the earlier routine, but could cause the same symptom.
pthread_mutex_lock(&data_mutex);
/* determine the offset at which the data will be written. this has to be accurate,
otherwise it could be causing the problem */
offset = ftell(current_fp);
fwrite(data);
fflush(current_fp);
pthread_mutex_unlock(&data_mutex);
There seem to be several places where things could go wrong. I would make the following changes: (1) be consistent and use the same I/O library as per bdonlan's suggestion, (2) make the lseek() and the writes an atomic action guarded by a mutex so that only a single thread at a time can do those actions of adding to both files. SEEK_CUR does a seek based on the current location of the file offset pointer so would you not want SEEK_END to seek to the end of the file in order to append there? Then if you are modifying a particular section of the file you would use SEEK_SET to reposition to the location you want to write to. And you would want to do this in a mutex guarded section so as to allow only a single thread to do the file positioning and file update.
If you're using your 'simpler routine' at the same time, this could indeed be a problem. If these are separate file descriptors, there's nothing to ensure that they're both pointing at the end of the file at all times (unless you use append mode, however I'm not sure what the semantics around ftell for append mode are). If they're the same fd (ie, you have a raw fd and a FILE * pointing to the same place), you might have problems with the standard library getting confused about where you are in a file, when you use write() to bypass it.
I wish to open a file using the "a+b" mode, i.e. if it does not exist it is created automatically, but if it does I don't want to overwrite it. I want to be able to read and write to the file.
The file is binary, and I want to save records of a specific struct in it. So I want to do fseek() to the record I want and then save the record using fwrite().
The code looks as follows (MyRecord is a typedef to a struct, while FILENAME is a #define to the file's name):
int saveRecord(MyRecord *pRecord, int pos)
{
FILE* file = fopen(FILENAME, "a+b");
if (file == NULL)
{
printf("Unable to open file %s\n", FILENAME);
return 0;
}
fseek(file, pos * sizeof(MyRecord), SEEK_SET);
fwrite(pRecord, sizeof(MyRecord), 1, file);
fclose(file);
return 1;
}
However this code just appends the record to the end of the file, even if I set pos to 0. Why isn't fseek() with SEEK_SET working in append mode?
I know I can simply open it with "r+b" and if it fails open it with "wb", but I want to know why this doesn't work and why fseek() with SEEK_SET is leaving the file pointer at the end. Any references to places where this behaviour is documented appreciated (because I couldn't find any, or I am using the wrong keywords).
That's because in a mode, writing to the FILE* always appends to the end. fseek only sets the read pointer in this mode. This is documented in the C standard, 7.19.5.3 fopen:
Opening a file with append mode ('a' as the first character in the mode argument)
causes all subsequent writes to the file to be forced to the then current end-of-file,
regardless of intervening calls to the fseek function.
Plain C does not have any sane way to achieve what you want. If you're on a POSIX system or anything remotely close, you can use fd=open(FILENAME, O_CREAT|O_RDRW, 0666) and then fdopen(fd, "rb+").
Edit: Another thing you could try, with plain C:
f = fopen(FILENAME, "a+b");
if (!f) /* ... */
tmp = freopen(0, "r+b", f);
if (tmp) f = tmp;
else /* ... */
Use "r+b" mode and fallback to "w+b" if it fails.
The "a+b" mode, allows you to read and append; the "r+b" allows random read and write.
The documentation for fopen describes how the file behaves with the different modes.