File read write mode "r+" unexpected behavior in C - c

In the following code, I am searching for a '.' in my template to paste a string after it. For some reason, although the string is pasted as expected, it deletes some text from my template. I do not have an idea where the problem could be. Tried fflush() with no good effect.
#include <stdio.h>
#include <string.h>
int main() {
FILE * fp;
int tmp_char, tmp_offset;
char file_name[50] = "FileIoTest_template.txt";
char tmp_string1[50] = "Generic String 1";
char tmp_string2[50] = "Generic String 2";
long tmp_long;
fp = fopen(file_name, "r+");
//fseek(fp, 0, SEEK_SET);
do {
tmp_char = fgetc(fp);
printf("%c ", tmp_char);
if (tmp_char == '.')
break;
} while (tmp_char != EOF);
tmp_long = ftell(fp);
fseek(fp, tmp_long, SEEK_SET);
tmp_offset = strlen(tmp_string1);
fputs(tmp_string1, fp);
fputs("\n", fp);
//fflush(fp);
fseek(fp, tmp_long+tmp_offset, SEEK_SET);
do {
tmp_char = fgetc(fp);
printf("%c ", tmp_char);
if (tmp_char == '.')
break;
} while (tmp_char != EOF);
tmp_long = ftell(fp);
fseek(fp, tmp_long, SEEK_SET);
fputs(tmp_string2, fp);
fputs("\n", fp);
//fflush(fp);
fclose(fp);
return 0;
}
Here is my template, "FileIoTest_template.txt":
Sample list:
1.
2.
3.
random text
4.
5.
6.
bunch of random text
The output of my code is:
Sample list:
1.Generic String 1
ext
4.Generic String 2
of random text

You cannot easily modify a file by inserting data into the middle of it without replacing anything that's already there. You would need to overwrite the whole file, from the insertion point to the end (past the original end to whatever point the new end needs to be). It is tricky to do so correctly, and it is unsafe to try, for if the process is interrupted in the middle then your file is trashed.
Usually, one instead creates a new version of the file contents in a temporary file, and once that is completed successfully, one replaces the original file with the new one.

Related

there is some text in a file and I have to put '\n' after every ending of a sentence. I am stuck

char text[1024];
fgets(text, 1024, stdin); //Foydanaluvchi kiritadi matn
FILE * file = fopen("my.txt", "w"); //yozish uchun file degan fayl ochiladi
for(int i=0; i<strlen(text); i++){
if(text[i] == 46){
fseek(file, 0, SEEK_CUR);
fputc(text[i]='\n', file);
}else{
fputc(text[i+1], file);
}
}
fclose(file);
Your logic does not make sense. Work it out on paper with a simple example to see that
here:
fputc(text[i]='\n', file);
you are writing a newline instead of the period, not after it; and
here:
fputc(text[i+1], file);
you are writing a different character than the one you just tested. Moreover,
this:
fseek(file, 0, SEEK_CUR);
asks to adjust the file position to its own current value. There may be circumstances where that's actually useful, but yours are not among them.
I think you're making it harder than it should be. All you really need to do to implement the behavior described is loop through the data, write each character, and if that character was a period ('.') then additionally write a newline.
Although the question does not say so, possibly you are expected to avoid adding extra newlines where there already is one after a sentence. That would require looking ahead at least one character, but it is doable.
You might also consider reading the file one character at a time (with getchar(), say). That would actually make your logic a little simpler, and it might smooth out some technical details if you have to avoid adding duplicate newlines, or if you need some other extra behavior such as eliminating space characters between sentences.
It is difficult to modify the file in place as you are trying to do, because there is no standard function or system call to insert bytes in the middle of a file.
The recommended approach it to write a simple filter that reads the text from stdin and writes the modified contents to stdout:
#include <stdio.h>
int main() {
int c;
while ((c = getchar()) != EOF) {
putchar(c);
if (c == '.')
putchar('\n');
}
return 0;
}
Note however that the above code will add a newline after each ., some of which might not end a sentence, eg: I paid $3.49 for each bug and some of which may already be followed by a newline, thereby doubling them.
To avoid both of the above pitfalls, you might only output the newline if the . is followed by a space. In this case, you can replace the space after each . with a newline, so you could operate in place on systems where newline is a single byte:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
FILE *fp;
int c, last = 0, count = 0;
if (argc < 2) {
fprintf(stderr, "missing file name\n");
return 1;
}
if ((fp = fopen(argv[1], "rb+")) == NULL) {
fprintf(stderr, "cannot open file %s: %s\n", argv[1], strerror(errno));
return 1;
}
while ((c = getc(fp)) != EOF) {
if (c == ' ' && last == '.') {
// call fseek to move back one byte
fseek(fp, -1L, SEEK_CUR);
// overwrite the space with a newline
putc('\n', fp);
// call fseek to allow switching back to read mode
fseek(fp, 0L, SEEK_CUR);
count++;
}
last = c;
}
fclose(fp);
printf("%d changes\n", count);
return 0;
}

Find end of text in a text file padded with NULL characters in C [duplicate]

file looks like this:
abcd
efgh
ijkl
I want to read the file using C so that it read the last line first:
ijkl
efgh
abcd
I cannot seem to find a solution that does not use an array for storage. Please help.
edit0:
Thanks for all the answers. Just to let you know, I am the one creating this file. So, can I create in a way its in the reverse order? Is that possible?
It goes like this:
Seek to one byte before the end of the file using fseek. There's no guarantee that the last line will have an EOL so the last byte doesn't really matter.
Read one byte using fgetc.
If that byte is an EOL then the last line is a single empty line and you have it.
Use fseek again to go backwards two bytes and check that byte with fgetc.
Repeat the above until you find an EOL. When you have an EOL, the file pointer will be at the beginning of the next (from the end) line.
...
Profit.
Basically you have to keep doing (4) and (5) while keeping track of where you were when you found the beginning of a line so that you can seek back there before starting your scan for the beginning of the next line.
As long as you open your file in text mode you shouldn't have have to worry about multibyte EOLs on Windows (thanks for the reminder Mr. Lutz).
If you happen to be given a non-seekable input (such as a pipe), then you're out of luck unless you want to dump your input to a temporary file first.
So you can do it but it is rather ugly.
You could do pretty much the same thing using mmap and a pointer if you have mmap available and the "file" you're working with is mappable. The technique would be pretty much the same: start at the end and go backwards to find the end of the previous line.
Re: "I am the one creating this file. So, can I create in a way its in the reverse order? Is that possible?"
You'll run into the same sorts of problems but they'll be worse. Files in C are inherently sequential lists of bytes that start at the beginning and go to the end; you're trying to work against this fundamental property and going against the fundamentals is never fun.
Do you really need your data in a plain text file? Maybe you need text/plain as the final output but all the way through? You could store the data in an indexed binary file (possibly even an SQLite database) and then you'd only have to worry about keeping (or windowing) the index in memory and that's unlikely to be a problem (and if it is, use a "real" database); then, when you have all your lines, just reverse the index and away you go.
In pseudocode:
open input file
while (fgets () != NULL)
{
push line to stack
}
open output file
while (stack no empty)
{
pop stack
write popped line to file
}
The above is efficient, there is no seek (a slow operation) and the file is read sequentially. There are, however, two pitfalls to the above.
The first is the fgets call. The buffer supplied to fgets may not be big enough to hold a whole line from the input in which case you can do one of the following: read again and concatenate; push a partial line and add logic to the second half to fix up partial lines or wrap the line into a linked list and only push the linked list when a newline/eof is encountered.
The second pitfall will happen when the file is bigger than the available ram to hold the stack, in which case you'll need to write the stack structure to a temporary file whenever it reaches some threshold memory usage.
The following code should do the necessary inversion:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *fd;
char len[400];
int i;
char *filename = argv[1];
int ch;
int count;
fd = fopen(filename, "r");
fseek(fd, 0, SEEK_END);
while (ftell(fd) > 1 ){
fseek(fd, -2, SEEK_CUR);
if(ftell(fd) <= 2)
break;
ch =fgetc(fd);
count = 0;
while(ch != '\n'){
len[count++] = ch;
if(ftell(fd) < 2)
break;
fseek(fd, -2, SEEK_CUR);
ch =fgetc(fd);
}
for (i =count -1 ; i >= 0 && count > 0 ; i--)
printf("%c", len[i]);
printf("\n");
}
fclose(fd);
}
The following works for me on Linux, where the text file line separator is "\n".
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void readfileinreverse(FILE *fp)
{
int i, size, start, loop, counter;
char *buffer;
char line[256];
start = 0;
fseek(fp, 0, SEEK_END);
size = ftell(fp);
buffer = malloc((size+1) * sizeof(char));
for (i=0; i< size; i++)
{
fseek(fp, size-1-i, SEEK_SET);
buffer[i] = fgetc(fp);
if(buffer[i] == 10)
{
if(i != 0)
{
counter = 0;
for(loop = i; loop > start; loop--)
{
if((counter == 0) && (buffer[loop] == 10))
{
continue;
}
line[counter] = buffer[loop];
counter++;
}
line[counter] = 0;
start = i;
printf("%s\n",line);
}
}
}
if(i > start)
{
counter = 0;
for(loop = i; loop > start; loop--)
{
if((counter == 0) && ((buffer[loop] == 10) || (buffer[loop] == 0)))
{
continue;
}
line[counter] = buffer[loop];
counter++;
}
line[counter] = 0;
printf("%s\n",line);
return;
}
}
int main()
{
FILE *fp = fopen("./1.txt","r");
readfileinreverse(fp);
return 0;
}
Maybe , The does the trick , It reverse content of the file in whole
just like a string
Define a variable of type string with size of your file
Get Contents of the file and store in the variable
Use strrev() to reverse the string.
You can later on display the output or even write it to a file. The code goes like this:
#include <stdio.h>
#include <String.h>
int main(){
FILE *file;
char all[1000];
// give any name to read in reverse order
file = fopen("anyFile.txt","r");
// gets all the content and stores in variable all
fscanf(file,"%[]",all);
// Content of the file
printf("Content Of the file %s",all);
// reverse the string
printf("%s",strrev(all));
fclose(file);
return 0;
}
I know this question has been awnsered, but the accepted awnser does not contain a code snippet and the other snippets feel too complex.
This is my implementation:
#include <stdio.h>
long file_size(FILE* f) {
fseek(f, 0, SEEK_END); // seek to end of file
long size = ftell(f); // get current file pointer
fseek(f, 0, SEEK_SET); // seek back to beginning of file
return size;
}
int main(int argc, char* argv[]) {
FILE *in_file = fopen(argv[1], "r");
long in_file_size = file_size(in_file);
printf("Got file size: %ld\n", in_file_size);
// Start from end of file
fseek(in_file, -1, SEEK_END); // seek to end of file
for (int i = in_file_size; i > 0; i--) {
char current_char = fgetc(in_file); // This progresses the seek location
printf("Got char: |%c| with hex: |%x|\n", current_char, current_char);
fseek(in_file, -2, SEEK_CUR); // Go back 2 bytes (1 to compensate)
}
printf("Done\n");
fclose(in_file);
}

Usage of fseek and feof

I this code is used for reading the text file in reverse order. And it successful does, displaying the original content of file and the reversed content of file.
#include <stdio.h>
#include <stdlib.h>
int main() {
int count = 0, ch = 0;
FILE *fp;
if( (fp = fopen("file.txt", "r")) == NULL ) {
perror("fopen");
exit(EXIT_FAILURE);
}
printf("\tINPUT FILE\n");
printf("\n");
while(!feof(fp)) {
if((ch = getc(fp)) != EOF) {
printf("%c", ch);
count ++;
}
}
feof(fp);
printf("\n");
printf("\tREVERSED INPUT FILE\n");
printf("\n");
while(count) {
fseek(fp, -2, SEEK_CUR);
printf("%c", getc(fp));
count--;
}
printf("\n");
fclose(fp);
}
But when i replaced, this piece of code
while(!feof(fp)) {
if((ch = getc(fp)) != EOF) {
printf("%c", ch);
count ++;
}
}
by
fseek (fp, 0, SEEK_END); or feof(fp);
Basically i just went till end of file and directly without printing the original contents of file and tried printing the reversed content of file.
But for it does not print the reversed content filed either !!! it just display blank. Why is this happening ??
NOTE: fseek(fp, -2, SEEK_CUR); Have done this (in another while loop) as getc(fp) moves fp forward by one so need to rewind it back by two, also initially it will be pointing to EOF
What is happening here? Can any one please explain?
It breaks because the second loop is while (count), and count is zero if you haven't read through the file first while incrementing it. You can use ftell to obtain the equivalent of count in this case.
P. S. feof(fp) only tests whether fp is at end-of-file, it does not make it seek to EOF, so the line feof(fp) basically does nothing since you aren't using the return value.
As #Arkku already showed, when you replace the while loop with fseek(SEEK_END), count will not be incremented.
To fix this, you can use ftell after fseek, which returns the file length
fseek(fp, 0, SEEK_END);
count = ftell(fp);
Now the file will be printed backwards.

Reverse file text

I'm trying to make a program that uses fgets to take the text from a preexisting file, invert it and then write it to another file. This is the code I've written so far:
#include <stdio.h>
#include <string.h>
int main()
{
int c, d;
FILE *file1, *file2;
char string [100], *begin, *end, temp;
file1 = fopen("StartingFile.txt", "rt");
if (file1 == NULL)
{
printf ("Error - Couldn't open file\n");
return (-1);
}
fgets(string, 100, file1);
fclose (file1);
begin = string;
end = begin + strlen(string) - 1;
while (end > begin)
{
temp = *begin;
*begin = *end;
*end = temp;
++begin;
--end;
}
file2 = fopen("FinalFile.txt", "wt");
fprintf (file2, "%s", string);
fclose (file2);
printf ("%s\n", string);
return 0;
}
It works fine if the text in the preexisting file is all in one line, but if it has more than one line, only the first one is inverted and written to the new file. I think that fgets can only read one line, so I think I'll have to use a loop, but I'm having trouble implementing it. Can someone give me a hand? Thanks in advance!
To read each line separately from file use fgets in while loop as below,
while(fgets(string, sizeof(string), file1) != NULL)
{
...
}
fclose(file1);
Inside the loop operate on each line to reverse it.
Your code has quite a few logical errors in it. I would recommend using other f* methods instead.
If you want an easy solution, open the file, determine its length, create two buffers of the size of the file, fill the first buffer with the file's contents and then do a loop to copy the reverse to the other buffer, then write that buffer back. Roughly that would look like this:
#include <stdio.h>
#include <string.h>
int main()
{
FILE *file;
file = fopen("StartingFile.txt", "rt");
if (file == NULL)
{
printf ("Error - Couldn't open file\n");
return (-1);
}
fseek(file, 0, SEEK_END); // move file pointer to end of file
long size = ftell(file); // file pointer position == character count in file
fseek(file, 0, SEEK_SET); // move back to beginning of file
char* buffer = malloc(size * sizeof(char));
fread(buffer, sizeof(char), size, file) // read file contents to buffer
for(long i = 0; i < size/2; ++i)
{
buffer[i] = buffer[size-i-1];
}
fseek(file, 0, SEEK_SET); // The fread set the file pointer to the end so we need to put it to the front again.
fwrite(buffer, sizeof(char), size, file); // Write reverted content
delete buffer;
fclose (file);
}
I haven't tested it and it may contain a few errors since I haven't programmed in C for some time. The only reason to still be programming in C anyways is efficiency and if you want your program to be efficient, the two buffer solution isn't the best either. At least not in terms of memory usage.
I highly recommend getting familiar with all the functions available in C (stdio and so on) cplusplus.com is a great reference for that.
Regards, Xaser

Using fread and fwrite in one stream at the same time caused a problem

This program want to read from a file. the content in the file is the string "Hello, world". then judge each character of the string to see if the character greater than or equal to the const character 'e', if the character meet the condition, than change the character to its previous character in the alphabetical order (eg. 'b' change to 'a', 'e' change to 'd'). Finally, output the changed file content to the screen.
The question is how do the fwrite and fread work? why can't I get rid off the variable pos2 to simplify the expression. If anyone can help, thanks a lot!
#include <stdio.h>
int main()
{
FILE *fp;
char s[20];
char t[20];
char transfer;
int i;
int pos; // storing the position of the file before reading from the file
int pos1; // check the position of the file
int pos2; // storing the position of the file after reading from the file
#pragma region create a file named "Hello", write "Hello, world" into the file, close it
if ((fp = fopen("Hello", "wb") )== NULL)
{
printf("can't open file\n");
exit(0);
}
strcpy(s, "Hello, world");
fwrite(s, sizeof(char)*20, 1, fp);
fseek(fp, 0, SEEK_SET);
fclose(fp);
#pragma endregion create a file named "Hello", write "Hello, world" into the file, close it
#pragma region read from the file named "Hello", deal with its current, write the change into the file.
if ((fp = fopen("Hello", "rb+")) == NULL )
{
printf("can't open file\n");
exit(1);
}
i = 0;
while(i < 20)
{
// 提问,该处为何不能利用fwrite的自动定位免去注释掉的语句行(即使用了pos2的语句行)。
// Here is the problem. since the fread and fwrite function can move the position of the
// file, I think I can get rid off the following commented two sentences with the
// variable pos2 in it
pos = ftell(fp); // storing the position before reading from file
fread(&transfer, sizeof(char), 1, fp); // the position of the file moved to the next char
// pos2 = ftell(fp); // storing the position after reading from file
pos1 = ftell(fp);
if (transfer >= 'e') // if the character greater or equal to 'e' minus 1.
{
transfer -= 1;
}
fseek(fp, pos, SEEK_SET); // back to the position where the character is read to change the char
fwrite(&transfer, sizeof(char), 1, fp);// the position of the file moved to the next char
// fseek(fp, pos2, SEEK_SET); //
pos1 = ftell(fp);
i++;
}
fseek(fp, 0, SEEK_SET);
fclose(fp);
#pragma endregion read from the file named "Hello", deal with its current, write the change into the file.
#pragma region read from the file named "Hello", output the changed string
if((fp = fopen("Hello", "rb")) == NULL)
{
printf("Can't open file\n");
exit(2);
}
fread(t, sizeof(char)*20, 1, fp);
printf("The output is: %s \n", t);
// the right output is (the two sentences above with pos2 in it is commented) :
// The output is: Hdkkn,vnqkd
// the wrong output is (the two sentences above with pos2 in it isn't commented):
// The output is: Hddddddddddddddddddd烫烫烫烫Hello, world
fseek(fp, 0, SEEK_SET);
fclose(fp);
#pragma endregion read from the file named "Hello", output the changed string
system("pause");
}
I didn't actually get the point why you are trying to comment in/out the 2 lines. Because nothing changes whether you comment them in or comment them out. You have already got rid of pos2 in your code (which is what you are asking).
So if you use the following code for your while loop
pos = ftell(fp); // storing the position before reading from file
fread(&transfer, sizeof(char), 1, fp);
pos1 = ftell(fp);
if (transfer >= 'e') // if the character greater or equal to 'e' minus 1.
{
transfer -= 1;
}
fseek(fp, pos, SEEK_SET);
fwrite(&transfer, sizeof(char), 1, fp);
i++;
then you get "The output is: Hdkkn,vnqkd" which is the expected result.
You could also take each line from the file to an array and make operations on it then write it back to the file. By this way, it could be more generic and you don't have to use magic numbers like "20".
EDIT:
I use gcc 4.5.2 on my linux platform. I don't want to comment on other platforms but as I mentioned before you could take the line to a buffer and then after operation you could write it back. You could try to replace the following code with your while loop:
char line[20] = {0};
fread(line, sizeof(char), 20, fp);
for(i = 0; i < strlen(line); i++)
{
if(line[i] >= 'e')
line[i] -= 1;
}
fseek(fp, 0, SEEK_SET);
fwrite(line, sizeof(char), strlen(line), fp);
By this way you could get rid of many variables. It is your choice.

Resources