Append item to a file before last line in c - c

The footer should update with current time and hour when a new Item added to the file.
Here is the sample file format,
ITEM1
ITEM2
06/07/2010 10:20:22 //footer line
The “item” has to append in the existing file, prior to the footer line, and the footer should be updated with new value.
I am having to variables, “item” and “time” which hold the respective values as string.
After adding a new item (ITEM3) the file looks like,
ITEM1
ITEM2
ITEM3
07/07/2010 17:20:12 //changed footer
Help me to acheive this.

Since the footer line is a static length...
Seek to the beginning of the footer line, write the new item over it followed by a newline, then write out the new footer.
(Note that this procedure is dangerous if you can't guarantee that you will always write at least enough total bytes to overwrite the existing footer, but it looks like you have exactly that assurance here.)

My solution would be, fopen with mode "r+" (reading and writing). fseek backwards from the end, a buffer length's worth and fill it (placing the cursor at the end again), search the buffer backwards for the last newline (if there is none, search even further back and repeat), once it has been found (or you hit the beginning of the file), search to that position in the file (your read placed it somewhere else), and write your new item, followed by a new line followed by a new footer. Close the file and truncate it to the right size.
HTH.
EDIT:
If the footer is always the same size, I'd definately go for Nicholas solution.

On a similar note; Whenever you modify a file, the file metadata changes to update the exact time of the last file write. You could always query that data with GetFileTime() on Windows.

It is easy. You only have to truncate the last line.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define NEWLINE "new line to append\n"
int main() {
FILE *file;
char *line = NULL;
size_t previous_line_offset = 0, line_offset = 0;
time_t time_signature;
// open data file
if((file = fopen("data.txt", "rw+")) == NULL) {
puts("ERROR: Cannot open the file");
return 1;
}
// find last line offset
while(!feof(file)) {
int n;
previous_line_offset = line_offset;
line_offset = ftell(file);
getline(&line, &n, file); // NOTE getline is a GNU extension
}
// append new line
fseek(file, previous_line_offset, SEEK_SET);
fputs(NEWLINE, file);
// apend signature
time(&time_signature);
fprintf(file, "%s", asctime(localtime(&time_signature)));
// free resources
fclose(file);
if(line)
free(line);
// voila!
return 0;
}

A simple (but not very efficient) algorithm could look like this:
string content = "";
string line = "";
// Append every but the last line to content
while(!eof(file))
{
content += line;
line = readline(file);
}
content += getNewItem();
content += generateFooter();
save(content);

Related

save a file modified using a list (c language)

=============================================================
edit : im sorry it seems like i havent explained well
what my program does is
open file for reading => load list from the file => operations chosen by user will be applies to list (not file)(add-search-display-reset)\ => import change to file(in case there is any)
if the user chooses to only add an employee and quit will it be better to append the added node to the end of file, close and free list or its okay to open file for reading and overwrite all the nodes from the beggining
this last option will save me a lot of lines of code but will it save time and energy for the user while execution ?
=============================================================
i am workig on a (c language) school project where we have to use both lists and files to :
display a list of employees
add employees
search an employee
reset list
save and quit
so i am looking for the best way to do it, and as i know a good code is the one that ensures the program uses as less memory as it can and be as fast as it can.
what i am asking you is
after opening the file that contains the infos of employees and loadig it to the list, and modifying the list by add or reset, would it be better to :
open the file for "w" and fill it with the list.
or open it for "a+" to add from last employee.
note that this last way i will have to memorize the initial_number_of_lines (aka nodes) and look for the 'initial last node' to start loading from it.
note : the employee variables are first_name last_name and salary; VERIFY(file) returns 1 i file opened returns 0 if not;
void load_file(list *list, char *filename)
{
if (current_number_of_lines != initial_number_of_lines || list_reset == 1)
{
if (current_number_of_lines > initial_number_of_lines && list_reset == 0)
{
FILE *file = fopen(filename, "a+");
if (VERIFY(file) == 1)
{
for (int i = 0; i < initial_number_of_lines; i++)
{
list = list->next;
}
while (list != NULL)
{
fprintf(file, "%s\t%s\t%lf\n", list->emp.Fname, list->emp.Lname, list->emp.salary);
list = list->next;
}
}
fclose(file);
}
else if (list_reset == 1)
{
FILE *file = fopen(filename, "w");
while (list != NULL)
{
fprintf(file, "%s\t%s\t%lf\n", list->emp.Fname, list->emp.Lname, list->emp.salary);
list = list->next;
}
fclose(file);
}
else
printf("Error\n");
}
freeList(list);
}
If all your add operations adds the new node to the end of the list, you will get better performance using a+. On the other hand, the code will be more simple if you always rewrite the the whole file. So what is most important for you? Performance or simple code? I would start with the simple approach.
Some other notes.
It's pretty strange that you call the function load_file when it actually writes the file.
The variables: current_number_of_lines, initial_number_of_lines, list_reset seem to be global variables. Using global variables is something you should avoid.
The logic (aka if statements) seems too complicated. You can simply do:
void load_file(list *list, char *filename)
{
if (list_reset == 1)
{
...
}
else if (current_number_of_lines > initial_number_of_lines)
{
...
}
else if (current_number_of_lines < initial_number_of_lines)
{
printf("Error\n");
}
freeList(list);
}
If you use "w" to fopen() then it will truncate file to zero length or create text file for writing. The stream is positioned at the beginning of the file. You have to write all records to the file.
If you use "a+" to fopen() then the file will be open for reading and appending (writing at end of file). The file is created if it does not exist. Output is always appended to the end of the file. Warning: POSIX is silent on what the initial read position is when using this mode. In the mode, you will only write records that needs to be added.
Please also note that neither of the methods are well suited for sharing the data between two or more applications.
If new records are added at the end of the list, obviously it is faster to append those records ate the end of the file ("a+" mode).
Since you have a text file (made of text lines), it is unfortunately not possible to update the changed records. In that case, you must write everything from the list to the file ("w" mode).
If you change you file format in order to use a fixed size record then you can optimize the writing by positioning the file on the record and then write. For that, you'll define your record as a struct containing fixed size strings. And of course add an item in that struct to remember where it has been read from the list.

Find and replace a line in a file

My goal is to search the file line by line until it finds a variable declaration in the format of varName = varValue. Count up the bytes to the beginning of that line, then replace that line with the same varName but a new value.
This is a very simple configuration file handler, I'm writing it from scratch to avoid any dependencies. The reason I'm doing it this way and not just dumping a string[string] associative array is because I want to preserve comments. I also wish to refrain from reading the entire file into memory, as it has the potential to get large.
This is the code I have written, but nothing happens and the file remains unchanged when using setVariable.
import std.stdio: File;
import std.string: indexOf, strip, stripRight, split, startsWith;
import std.range: enumerate;
ptrdiff_t getVarPosition(File configFile, const string varName) {
size_t countedBytes = 0;
foreach (line, text; configFile.byLine().enumerate(1)) {
if (text.strip().startsWith(varName))
return countedBytes;
countedBytes += text.length;
}
return -1;
}
void setVariable(File configFile, const string varName, const string varValue) {
ptrdiff_t varPosition = getVarPosition(configFile, varName);
if (varPosition == -1)
return; // For now, just return. This variable doesn't exist.
// Will handle this later, it needs to append to the bottom of the file.
configFile.seek(varPosition);
configFile.write(varName ~ " = " ~ varValue);
}
There's a few parts of your code missing, which makes diagnosis hard. The most important question may be 'How do you open your config file?'. This code does what I expect it to:
unittest {
auto f = File("foo.txt", "r+");
setVariable(f, "var3", "foo");
f.flush();
}
That is, it finds the line starting with "var3", and replaces part of the file with the new value. However, your getVarPosition function doesn't count newlines, so the offset is wrong. Also, consider what happens when the new varValue is a different length from the old value. If you have "var = hello world", and call setVariable(f, "var", "bye"), you'll end up with "var = byelo world". If it's longer than the existing value, it will overwrite the next variable(s).

The last character is not printed to a file

I am trying to figure out why using C function strtok is not working properly for me. Here's the problem:
I have a file which contains two types of information: headers and text descriptions. Each line in the file is either a header or part of a text description. A header starts with '>'. The description text follows the header and can span multiple lines. At the end of the text there is an empty line which separates the description from the next header. My aim is to write two separate files: one contains the headers on each line and the other contains the corresponding description on a line by itself. To implement the codes in C, I used fgets to read the file one line at a time into dynamically allocated memory. In order to write the description text on one single line, I used `strtok to get rid of any new line characters exists in the text.
My code is working properly for the header files. However, for the descriptions file, I noticed that the last character of the text is not printed out to the file even though it is printed to the stdout.
FILE *headerFile = fopen("Headers", "w"); //to write headers
FILE *desFile = fopen("Descriptions", "w"); //to write descriptions
FILE *pfile = fopen("Data","r");
if ( pfile != NULL )
{
int numOfHeaders =0;
char **data1 = NULL; //an array to hold a header line
char **data2 = NULL; //an array to hold a description line
char line[700] ; //maximum size for the line
while (fgets(line, sizeof line, pfile ))
{
if(line[0] =='>') //It is a header
{
data1 = realloc(data1,(numOfHeaders +1)* sizeof(*data1));
data1[numOfHeaders]= malloc(strlen(line)+1);
strcpy(data1[numOfHeaders],line);
fprintf(headerFile, "%s",line);//writes the header
if(numOfHeaders >0)
fprintf(desFile, "\n");//writes a new line in the desc file
numOfHeaders++;
}
//it is not a header and not an empty line
if(line[0] != '>' && strlen(line)>2)
{
data2 = realloc(data2,(numOfHeaders +1)* sizeof(*data2));
data2[numOfHeaders]= malloc(strlen(line)+1);
char *s = strtok(line, "\n ");
strcpy(data2[numOfHeaders],s);
fprintf(desFile, "%s",data2[numOfHeaders]);
printf(desFile, "%s",data2[numOfHeaders]);
}
} //end-while
fclose(desFile);
fclose(headerFile);
fclose(pfile );
printf("There are %d headers in the file.\n",numOfHeaders);
}
As mentioned in the comments:
fprintf(desFile, "%s",data2[numOfHeaders]); //okay
printf(desFile, "%s",data2[numOfHeaders]); //wrong
Second line should be:
printf("%s",data2[numOfHeaders]); //okay
Or, you could do this:
sprintf(buffer, "%s",data2[numOfHeaders]);
fprintf(desFile, buffer);
printf(buffer);
Other possible issues:
Without an input file it is not possible to know for certain what strtok() is doing, but here is a guess based on what you have described:
In these two lines:
data2[numOfHeaders]= malloc(strlen(line)+1);
char *s = strtok(line, "\n ");
if the string contained in data2 has any embedded spaces, s will only contain the segment occurring before that space. And because you are only calling it once before line gets refreshed:
while (fgets(line, sizeof line, pfile ))
only one token (the very first segment) will be read.
Not always, but Normally, strtok() is called in a loop:
char *s = {0};
s= strtok(stringToParse, "\n ");//make initial call before entering loop
while(s)//ALWAYS test to see if s contains new content, else NULL
{
//do something with s
strcpy(data2[numOfHeaders],s);
//get next token from string
s = strtok(NULL, "\n ");//continue to tokenize string until s is null
}
But, as I said above, you are calling it only once on that string before the content of the string is changed. It is possible then, that the segment not printing has simply not yet been tokenized by strtok().

Bus Error on void function return

I'm learning to use libcurl in C. To start, I'm using a randomized list of accession names to search for protein sequence files that may be found hosted here. These follow a set format where the first line is a variable length (but which contains no information I'm trying to query) then a series of capitalized letters with a new line every sixty (60) characters (what I want to pull down, but reformat to eighty (80) characters per line).
I have the call itself in a single function:
//finds and saves the fastas for each protein (assuming on exists)
void pullFasta (proteinEntry *entry, char matchType, FILE *outFile) {
//Local variables
URL_FILE *handle;
char buffer[2] = "", url[32] = "http://www.uniprot.org/uniprot/", sequence[2] = "";
//Build full URL
/*printf ("u:%s\nt:%s\n", url, entry->title); /*This line was used for debugging.*/
strcat (url, entry->title);
strcat (url, ".fasta");
//Open URL
/*printf ("u:%s\n", url); /*This line was used for debugging.*/
handle = url_fopen (url, "r");
//If there is data there
if (handle != NULL) {
//Skip the first line as it's got useless info
do {
url_fread(buffer, 1, 1, handle);
} while (buffer[0] != '\n');
//Grab the fasta data, skipping newline characters
while (!url_feof (handle)) {
url_fread(buffer, 1, 1, handle);
if (buffer[0] != '\n') {
strcat (sequence, buffer);
}
}
//Print it
printFastaEntry (entry->title, sequence, matchType, outFile);
}
url_fclose (handle);
return;
}
With proteinEntry being defined as:
//Entry for fasta formatable data
typedef struct proteinEntry {
char title[7];
struct proteinEntry *next;
} proteinEntry;
And the url_fopen, url_fclose, url_feof, url_read, and URL_FILE code found here, they mimic the file functions for which they are named.
As you can see I've been doing some debugging with the URL generator (uniprot URLs follow the same format for different proteins), I got it working properly and can pull down the data from the site and save it to file in the proper format that I want. I set the read buffer to 1 because I wanted to get a program that was very simplistic but functional (if inelegant) before I start playing with things, so I would have a base to return to as I learned.
I've tested the url_<function> calls and they are giving no errors. So I added incremental printf calls after each line to identify exactly where the bus error is occurring and it is happening at return;.
My understanding of bus errors is that it's a memory access issue wherein I'm trying to get at memory that my program doesn't have control over. My confusion comes from the fact that this is happening at the return of a void function. There's nothing being read, written, or passed to trigger the memory error (as far as I understand it, at least).
Can anyone point me in the right direction to fix my mistake please?
EDIT: As #BLUEPIXY pointed out I had a potential url_fclose (NULL). As #deltheil pointed out I had sequence as a static array. This also made me notice I'm repeating my bad memory allocation for url, so I updated it and it now works. Thanks for your help!
If we look at e.g http://www.uniprot.org/uniprot/Q6GZX1.fasta and skip the first line (as you do) we have:
MNAKYDTDQGVGRMLFLGTIGLAVVVGGLMAYGYYYDGKTPSSGTSFHTASPSFSSRYRY
Which is a 60 characters string.
When you try to read this sequence with:
//Grab the fasta data, skipping newline characters
while (!url_feof (handle)) {
url_fread(buffer, 1, 1, handle);
if (buffer[0] != '\n') {
strcat (sequence, buffer);
}
}
The problem is sequence is not expandable and not large enough (it is a fixed length array of size 2).
So make sure to choose a large enough size to hold any sequence, or implement the ability to expand it on-the-fly.

deleting a string from a particular position in a file

I want to delete a string from a particular position in the file. Is thre a function to do that?
Can I delete last line of a file through a function?
You have two option
To read whole file, remove what you need and write it back
If the file is big, read file sequentially, remove given part, and move content after that forward
No there is no such function that will let you do this directly on a file.
You should load up the file content in memory and modify there and write back to file.
I don't feel like looking up all the io functions, so here's pseudo-c on how to implement option 2 of ArsenMkrt's answer
char buffer[N]; // N >= 1
int str_start_pos = starting position of the string to remove
int str_end_pos = ending position of the string to remove
int file_size = the size of the file in bytes
int copy_to = str_start_pos
int copy_from = str_end_pos + 1
while(copy_from < file_size){
set_file_pos(file, copy_from)
int bytes_read = read(buffer, N, file)
copy_from += bytes_read
set_file_pos(file, copy_to)
write(buffer, file, bytes_read)
copy_to += bytes_read
}
truncate_file(file,file_size - (str_end_pos - str_start_pos + 1))
something to that effect

Resources