Running is fine but rename() does not work - c

Simple function code to delete a line from a text file by making a temporary text file that will store the new content once the line has been deleted and replacing the old Storage.txt file with the temporary file.
The delete() function works but my only problem seems to be the rename() function that seemingly won't do as intended.
THE CODE
void delete() {
struct task task;
FILE *fp;
char str[100];
char ch;
int delete_line = 0;
fp = fopen("Storage.txt", "r");
if (fp == NULL) {
printf("Error opening file");
fopen("Storage.txt", "w");
exit (1);
}
printf("\n\n\nAll Tasks\n");
printf("----------\n\n");
do {
ch = fgetc(fp);
printf("%c", ch);
} while (ch != EOF);
fclose(fp);
int line_no,ret;
char filename[] = "Storage.txt";
char newname[] = "temp.txt";
FILE *file, *temp;
file = fopen("Storage.txt", "r");
temp = fopen("temp.txt", "w");
printf("Select Line to delete: ");
scanf("d", &delete_line);
getchar();
temp = fopen("temp.txt", "w");
while (fgets(str, 99, fp) != NULL) {
line_no++;
if (line_no != delete_line) {
fputs(str, temp);
}
}
fclose(file);
fclose(temp);
remove(filename);
ret = rename(newname, filename);
if (ret == 0) {
printf("File renamed successfully");
} else {
printf("Error: unable to rename the file");
}
}

There are some problems in the code:
ch must be defined with type int to detect EOF reliably.
the do/while loop to read the file contents outputs the EOF indicator before testing it. You should use while ((ch = fgetc(fp)) != EOF) putchar(ch);
the identifier delete should be avoided to avoid confusing C++ programmers, use delete_line instead.
you should test for failure of fopen and remove and display the cause of the error.
if opening the file for reading fails, why do you create the file with fopen("Storage.txt", "w") ?
file temp.txt is open twice, which may prevent the rename operation on legacy systems.
line_no is not initialized. It should be initialized to 1 if lines are numbered starting at 1.
reading lines into an array is not reliable for this task as lines longer than 99 bytes will be counted more than once.
Here is a modified version:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void delete_line() {
const char *filename = "Storage.txt";
const char *tempname = "temp.txt";
int ch;
FILE *fp = fopen(filename, "r");
if (fp == NULL) {
fprintf(stderr, "Cannot open file %s: %s\n", filename, strerror(errno));
exit(1);
}
printf("\n\n\nAll Tasks\n");
printf("----------\n\n");
while ((ch = getc(fp)) != EOF) {
putchar(ch);
}
fclose(fp);
int delete_line = 0;
printf("Select Line to delete: ");
if (scanf("d", &delete_line) != 1) {
fprintf(stderr, "invalid or missing input\n");
exit(1);
}
// read and discard the rest of the user input line
while ((ch = getchar()) != EOF && c != '\n')
continue;
FILE *file = fopen(filename, "r");
if (file == NULL) {
fprintf(stderr, "Cannot open file %s: %s\n", filename, strerror(errno));
exit(1);
}
FILE *temp = fopen(tempname, "w");
if (temp == NULL) {
fprintf(stderr, "Cannot open file %s: %s\n", tempname, strerror(errno));
fclose(file);
exit(1);
}
int line_no = 1;
while ((ch = getc(file)) != EOF) {
if (line_no != delete_line)
putc(ch, temp);
if (ch == '\n')
line_no++;
}
fclose(file);
fclose(temp);
if (remove(filename)) {
fprintf(stderr, "Cannot remove %s: %s\n", filename, strerror(errno));
} else {
if (rename(tempname, filename)) {
fprintf(stderr, "Cannot rename %s as %s: %s\n",
tempname, filename, strerror(errno));
}
}
}

Your code opens the "temp.txt" file twice:
temp = fopen("temp.txt", "w");
...
temp = fopen("temp.txt", "w");
And closes it once. That will leave one open file descriptor to the file, untill the program exits.
remove() uses unlink() for deleting files. The man page of unlink() says:
If the name was the last link to a file but any processes still have
the file open the file will remain in existence until the last file
descriptor referring to it is closed.
Ensure that all file descriptors are closed when not needed anymore.
The rename may fail, if file of oldpath or newpath is still open.

temp = fopen("temp.txt", "w"); Call it twice

The two main bugs here are:
1.
scanf("d", ...) instead of
scanf("%d", ...)
scanf() needs a format string to know how to parse the input, just like printf() (the f is for format) needs it to know how to construct the output; and their format string syntax is almost the same.
2.
Unintialized line_no, meaning that it's not guaranteed to start at 0/1, thus it might not ever be equal to delete_line, and will not delete the line.

Related

How to use strstr() function?

I have two files blacklist.txt and email.txt. The blacklist.txt contains some domain names. The email.txt also contains a few domain names. I have to compare both files and find the domain names of blacklist.txt into email.txt using the strstr() function. Following is the code I have written. The problem with this code is it returns me the output NULL instead of the matched text/domain name.
#include <stdio.h>
#include <string.h>
#define MAXCHAR 1000
int main() {
FILE *fp, *fp1;
char str[MAXCHAR];
char str2[MAXCHAR];
char *result;
fp = fopen("blacklist.txt", "r");
fp1 = fopen("email.txt", "r");
if (fp == NULL || fp1 == NULL) {
printf("\n Cannot open one of the files %s %s \n", fp, fp1);
//return = 0;
if (fp != NULL) fclose(fp);
if (fp1 != NULL) fclose(fp1);
}
while (fgets(str, MAXCHAR, fp) != NULL || fgets(str2, MAXCHAR, fp1) != NULL)
//printf("%s, %s", str,str2);
fclose(fp);
fclose(fp1);
result = strstr(str, str2);
printf("The substring starting from the given string: %s", result);
return 0;
}
Here are some remarks on your code:
printing the error message has undefined behavior because you pass the FILE* pointers instead of the file names.
your program has undefined behavior because the body of the while loop is missing.
Since one cannot assume that both files be sorted, each line from blacklist.txt should be tested against all lines in email.txt.
if we can assume that both lines end with a newline, a match with strstr() means the line from the second file s a suffix of the line from the first file. It is a domain match if the return value is the start of the buffer and it is a match for a subdomain if the previous character is a ..
Here is a modified version:
#include <stdio.h>
#include <string.h>
#define MAXCHAR 1000
int main() {
FILE *fp, *fp2;
char str[MAXCHAR];
char str2[MAXCHAR];
// open and check blacklist.txt
fp = fopen("blacklist.txt", "r");
if (fp == NULL) {
printf("Cannot open %s\n", "blacklist.txt");
return 1;
}
// open and check email.txt
fp2 = fopen("email.txt", "r");
if (fp2 == NULL) {
printf("Cannot open %s\n", "email.txt");
fclose(fp);
return 1;
}
// for each line in blacklist.txt
while (fgets(str, MAXCHAR, fp) != NULL) {
// restart from the beginning of email.txt
rewind(fp2);
// for each line of email.txt
while (fgets(str2, MAXCHAR, fp2) != NULL) {
// check for a domain match
char *p = strstr(str, str2);
if (p != NULL && (p == str || p[-1] == '.')) {
// compute the length of the domains (excluding the newline)
int n = strcspn(str, "\n");
int n2 = strcspn(str2, "\n");
// output the message with the matching domains
printf("domain match on %.*s for %.*s\n", n2, str2, n, str);
}
}
}
fclose(fp);
fclose(fp2);
return 0;
}

Can't pass a char array (file name) to fopen(file,r)

I want users to enter their own file name they want into the program, then the program opens it. I don't know why it doesn't work, please help. It just works if I define the path directly on the code.
This works.
FILE *file;
file = fopen("C:\\Users\\Test\\text.txt", "r");
if (file) {
index = 0;
while ((c = getc(file)) != EOF) {
printf("ok");
}
fclose(file);
} else {
printf("Can't open file");
}
This doesn't work.
char inputFile[100];
printf("Enter file name: ");
scanf("%s", inputFile);
FILE *file;
file = fopen(inputFile, "r");
if (file) {
index = 0;
while ((c = getc(file)) != EOF) {
printf("ok");
}
fclose(file);
} else {
printf("Can't open file");
}
When reading in from code, you need to escape your "\", but scanf isn't as smart. You just need to enter the text as is!
C:\Users\Test\text.txt
Also, note that scanf ("%s", inputFile) won't handle spaces in the file name path, so "My Documents" won't work.

C Programming- How to delete a line containing a specific word from file?

I have created a stock inventory program for a jewelry store.
However, I cannot seem to figure out how to search for a specific word in a file containing the items (in a table form) and delete the entire line which the word was on.
For example, I can delete earring from the list but not the quantity as well which would be a few spaces away on the same line.
#include <stdio.h>
int main() {
FILE *fp1, *fp2;
//consider 40 character string to store filename
char filename[40];
char c;
int del_line, temp = 1;
//asks user for file name
printf("Enter file name: ");
//receives file name from user and stores in 'filename'
scanf("%s", filename);
//open file in read mode
fp1 = fopen(filename, "r");
c = getc(fp1);
//until the last character of file is obtained
while (c != EOF) {
printf("%c", c);
//print current character and read next character
c = getc(fp1);
}
//rewind
rewind(fp1);
printf(" \n Enter line number of the line to be deleted:");
//accept number from user.
scanf("%d", &del_line);
//open new file in write mode
fp2 = fopen("copy.c", "w");
c = getc(fp1);
while (c != EOF) {
c = getc(fp1);
if (c == '\n')
temp++;
//except the line to be deleted
if (temp != del_line) {
//copy all lines in file copy.c
putc(c, fp2);
}
}
//close both the files.
fclose(fp1);
fclose(fp2);
//remove original file
remove(filename);
//rename the file copy.c to original name
rename("copy.c", filename);
printf("\n The contents of file after being modified are as follows:\n");
fp1 = fopen(filename, "r");
c = getc(fp1);
while (c != EOF) {
printf("%c", c);
c = getc(fp1);
}
fclose(fp1);
return 0;
}
Your code has several issues:
You read the filename with an unprotected call to scanf, if the name typed by the user exceeds 39 characters, you invoke undefined behavior. Make the buffer larger as filenames tend to be long nowadays and protect the format this way:
char filename[256];
if (scanf("%255s", filename) != 1) {
/* end of file reached, deal with this error */
...
}
Your method for reading a file byte by byte is incorrect: c should be defined as an int otherwise you cannot reliably detect EOF. The standard idiom for this is:
int c;
//until the last character of file is obtained
while ((c = getc(fp1)) != EOF) {
//print current character and read next character
putchar(c);
}
In the copy phase, you drop the first character of the file.
You ignore errors. Not only do you invoke undefined behavior, you may actually erase the file after failing to copy its contents.
You ask for a line number from the user, but you do not show the line numbers in the echo phase. Doing so would help.
Here is a improved version:
#include <stdio.h>
int main(void) {
FILE *fp1, *fp2;
//consider 255 character string to store filename
char filename[256];
int c, last, del_line, lineno;
//asks user for file name
printf("Enter file name: ");
//receives file name from user and stores in 'filename'
if (scanf("%255s", filename) != 1) {
perror("missing filename");
return 1;
}
//open file in read mode
fp1 = fopen(filename, "r");
if (fp1 == NULL) {
perror("cannot open file");
return 1;
}
//until the last character of file is obtained
last = '\n';
lineno = 0;
while ((c = getc(fp1)) != EOF) {
if (last == '\n') {
printf("%4d: ", ++lineno);
}
//print current character and read next character
putchar(c);
last = c;
}
rewind(fp1);
printf("\nEnter line number of the line to be deleted: ");
//accept number from user.
if (scanf("%d", &del_line) != 1) {
perror("missing line number");
return 1;
}
if (del_line < 1 || del_line > lineno) {
printf("no such line: %d\n", del_line);
return 1;
}
//open new file in write mode
fp2 = fopen("copy.c", "w");
if (fp2 == NULL) {
perror("cannot open copy.c");
return 1;
}
lineno = 1;
while ((c = getc(fp1)) != EOF) {
//except the line to be deleted
if (lineno != del_line) {
//copy all lines in file copy.c
putc(c, fp2);
}
if (c == '\n')
lineno++;
}
//close both files.
fclose(fp1);
if (fclose(fp2)) {
perror("write error to copy.c");
return 1;
}
// remove original file (unsafe)
// uncomment this if your system does not allow rename
// to overwrite existing files
// if (remove(filename)) {
// perror("cannot remove source file");
// return 1;
// }
//rename the file copy.c to original name
if (rename("copy.c", filename)) {
perror("cannot rename file");
return 1;
}
printf("\nThe contents of file after being modified are as follows:\n");
fp1 = fopen(filename, "r");
if (fp1 == NULL) {
perror("cannot re-open modified file");
return 1;
}
while ((c = getc(fp1)) != EOF) {
putchar(c);
}
fclose(fp1);
return 0;
}

Read one file and write to another C

I am trying to read from files and write to a temp file. However, I am stuck in an infinite loop right now. The function below is called multiple times by a recursive function that goes through directories to read files.
My approach is that I would read each word from one file, then those words to another file.
The function I have works fine if I just print out each word. It prints out each word in each file in all directories. However, when I try to start writing to a temp file (the code commented out), I am stuck in the while loop.
On the other hand, if I just call the function once in a test program where I just read from one file in the current directory, and write to a temp file, it's fine.
This is what I have (fileName when passed in is actually the absolute path, and I do ../tmp so it does not get caught in the recursion function):
void fileReadWrite(char *pattern, char *before, char *replace, char *fileName) {
FILE *file = fopen(fileName, "r");
if (file != NULL) {
int ch, word = 0;
while ((ch = fgetc(file)) != EOF) {
if (isspace(ch) || ispunct(ch)) {
if (word) {
word = 0;
putchar('\n');
}
}
else {
word = 1;
putchar(ch);
/*
FILE *f = fopen("../tmp", "wb"); // create and write
if (f == NULL)
{
printf("Error opening file!\n");
exit(1);
}
fprintf(f, "Some text"); // Or fprintf(f, ch);
fclose(f);
*/
}
}
fclose(file);
}
}
There's nothing in your code that suggests an infinite loop. However, if fileName is very large, you could be opening and closing "..\tmp" millions of times. As Joachim Pileborg points out in the comments, you should open that file just once at the beginning of your function, and close it again at the end.
If you want to convince yourself that you are not in an infinite loop, print out the value of ch on each iteration.
Okay so I did this and it worked. But I don't understand why though. Can someone explain it please?
void fileReadWrite(char *pattern, char *before, char *replace, char *fileName) {
FILE *file = fopen(fileName, "r");
FILE *f = fopen("../tmp", "wb"); // MOVE HERE
if (file != NULL) {
int ch, word = 0;
while ((ch = fgetc(file)) != EOF) {
if (isspace(ch) || ispunct(ch)) {
if (word) {
word = 0;
putchar('\n');
}
}
else {
word = 1;
putchar(ch);
/*
if (f == NULL)
{
printf("Error opening file!\n");
exit(1);
}
fprintf(f, "Some text"); // Or fprintf(f, ch);
*/
}
}
fclose(file);
fclose(f); // MOVE HERE
}
}

Adding a file close statement causes a seg fault

When trying to close the file after reading it, I get a seg fault on running the program.
int inputDirectory()
{
char fileName[64];char directoryBuffer[256];FILE *fp;
printf("\n> Please type the filename containing the list of directories. >");
inputFix(fileName, sizeof(fileName));
fp = fopen(fileName,"r");
if(access(fileName, F_OK) == 0)
{
if (fp == 0)
{
printf("> Error opening file.");
return 1;
}
else
{
if (access(fileName, R_OK) == 0)
{
while (fgets(directoryBuffer, sizeof(directoryBuffer), (FILE*)fp))
{
readCheck(directoryBuffer);
printf("%s \n", directoryBuffer);
getInode(directoryBuffer);
}
}
else
{
printf("\n> File can't be read.");
}
}
}
else
{
printf("\n> File %s does not exist ", fileName);
}
fclose(fp);
return 0;
}
void inputFix(char string[],int length)
{
int ch, len = 0;
fgets(string, length, stdin);
string[strcspn(string, "\r\n")] = '\0';
len = strlen(string);
if (len == length - 1)
{
while((ch = getchar()) != '\n' && ch != EOF);
}
}
void readCheck(char string[])
{
string[strcspn(string, "\r\n")] = '\0';
}
Ive been reading into race conditions, but from my understanding there isn't one? Is there a need to check to see if the file exists before trying to open it? Is there a need to include some of the checks that I'm using?
Looking at these lines.
if (fp == 0)
{
printf("> Error opening file.");
fclose(fp); // NOT NEEDED. REMOVE THE LINE
}
It seems you don't need to call fclose when you were not able to open the file.
Remove the line.
If fp is null (equal to 0), you do not need to close it, the file was never opened to begin with. You should close fp after you are done successfully reading from it.
You are closing the file at the end regardless of whether the file ever opened or not. Calling fclose on an unopened file can cause a crash. Try this instead. I have moved the fclose statement to be called only when fp is not NULL.
int inputDirectory()
{
char fileName[64];char directoryBuffer[256];FILE *fp;
printf("\n> Please type the filename containing the list of directories. >");
inputFix(fileName, sizeof(fileName));
if(access(fileName, F_OK) == 0)
{
fp = fopen(fileName,"r");
if (fp == NULL)
{
printf("> Error opening file.");
return 1;
}
else
{
if (access(fileName, R_OK) == 0)
{
while (fgets(directoryBuffer, sizeof(directoryBuffer), (FILE*)fp))
{
readCheck(directoryBuffer);
printf("%s \n", directoryBuffer);
getInode(directoryBuffer);
}
}
else
{
printf("\n> File can't be read.");
}
fclose(fp);
}
}
else
{
printf("\n> File %s does not exist ", fileName);
}
return 0;
}
Only call fclose() on a FILE* which had been returned by a successful call to fopen().
To test wether fopen() had been successful compare its result against NULL. If this test succeeds the call had not been successful:
#include <stdio.h>
int main(void)
{
char filename[] = "myfile";
FILE * fp = fopen(filename, "r");
if (NULL == fp)
{
fprintf(stderr, "fopen(\"%s\", ...) failed.\n", filename);
}
else
{
fprintf(stderr, "fopen(\"%s\", ...) succeeded.\n", filename);
/* Perform operation on fp here. */
fclose(fp);
}
return 0;
}

Resources