Related
I am using C to work on a program that manages structs in a binary file.
I am having issues with my deleteData()function.
The method gets file name from user, validates file name, asks for record id to be deleted, then it copies the records except the one to be deleted from a file it is reading to a temp file.
The function should then close files, delete old file, and rename new file by the old file's name.
Everything appears to work except I can't open the new renamed file. I have checked the outcome in every step and it all appears to be ok.
Furthermore, when I look in my folder, I can see the renamed file and from it's byte size I can tell that a record has indeed been deleted.
But I can't open the file.
Please see my code below. I am working on a MAC, I have saved the folder that includes the code on my desktop. I am new to programming so any feedback or advise would be greatly appreciated.
void
deleteData()
{
char studentID[10];
char fileName5[30];
char tmpFile[] = "tmp.bin";
FILE *file;
FILE *tmpf;
int erFlag = 0;
int foFlag = 0;
int t;
struct Record tmp;
do {
printf(" Enter file in which record is located:\n");
scanf("%30s", fileName5);
if (!valSuf(fileName5))
printf("Error wih file name.\n");
} while (!valSuf(fileName5));
file = fopen(fileName5, "rb");
if (file == NULL)
printf("Error opening file.");
if (fread(&t, sizeof(int), 1, file) != 1)
printf("error reading total");
tmpf = fopen(tmpFile, "wb");
if (tmpf == NULL)
printf("Error opening temp file.");
if (fwrite(&t, sizeof(int), 1, tmpf) != 1)
printf("Error writing total.");
printf("Enter student ID you want to delete:");
scanf("%30s", studentID);
scanf("%*[^\n]");
while (fread(&tmp, sizeof(Record), 1, file) != 0) {
printf("%s\n", tmp.studentId);
if (strcmp(tmp.studentId, studentID) != 0) {
fwrite(&tmp, sizeof(Record), 1, tmpf);
printf("written to file: %s\n", tmp.studentId);
}
else {
foFlag = 1;
}
}
if (foFlag == 1) {
printf("Record not found.\n");
fclose(tmpf);
fclose(file);
remove(tmpFile);
}
else {
printf("in final stage.\n");
printf("closing file:%d\n", fclose(file));
printf("closing tmp: %d\n", fclose(tmpf));
printf("removing old file:%d\n", remove(fileName5));
printf("renaming file: %d\n", rename(tmpFile, fileName5));
}
}
Some issues ...
You do scanf("%30s", studentID); but you have [only] char studentID[10];
Your logic is reversed. In the loop, you set foFlag to 1 if you match [and delete] a record. But, if (foFlag == 1) should be if (foFlag == 0).
On POSIX systems, you shouldn't call remove before doing rename. Note that rename is designed to atomically unlink the old file and replace it with the new file. That is, observers [other processes] will see either the old contents or the new ones, but, never a partial or missing file.
You say that the resultant file has the shorter length but is unreadable. I don't see how that could be [from this code] unless you have an unusual umask value:
At the shell prompt, enter the umask command to see the value. It should normally report 22. If not, do umask 22 to set.
Try using ls -l to see what permissions are on the file.
UPDATE:
Thank you. 1. You are right. 2. Right again, I changed it to run a test and didn't change it back before posting. 3. I have now tried renaming FileName5 to whatever.bin, then I renamed tmpFile -> FileName5. Then I removed whatever.bin (after renaming) but the issue persists...... The permissions are as follows: rw-r--r--. sorry if this is a silly question, but is that not normal? –
J. svatan.
The rw-r--r--. is normal. That is what we'd expect with a umask value of 22. That is, when doing an fopen(file,"wb"); the resultant file will allow read access to all and write access to the owner--what we want.
So, the file should be readable. Even if the ownership was messed up (e.g. unlikely, but if file is owned by root but you are user "me"), it should still allow read access because the file is "world/other" readable.
How are you testing for readability after creating the file? You could just do: fopen and then fclose.
I've refactored your program to be more diagnostic in nature. All/most returns are checked.
I made it create a file, delete some records, and show the file contents:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define sysfault(_fmt...) \
do { \
printf(_fmt); \
exit(1); \
} while (0)
struct Record {
char studentID[10];
int data;
};
void
xwrite(const void *buf,size_t buflen,FILE *fout)
{
size_t xlen;
xlen = fwrite(buf,1,buflen,fout);
if (xlen != buflen)
sysfault("xwrite: unable to write buf=%p buflen=%zu xlen=%zu -- %s\n",
buf,buflen,xlen,strerror(errno));
}
int
makerec(FILE *fout,int data,const char *studentID)
{
struct Record rec;
size_t len = strlen(studentID);
rec.data = data;
if (len >= sizeof(rec.studentID))
sysfault("makerec: studentID too long -- '%s'\n",studentID);
strcpy(rec.studentID,studentID);
xwrite(&rec,sizeof(rec),fout);
return 1;
}
void
makefile(const char *file)
{
FILE *fout = fopen(file,"wb");
if (fout == NULL)
sysfault("makefile: unable to open '%s' -- %s\n",file,strerror(errno));
int t = 0;
// write out phony count
xwrite(&t,sizeof(t),fout);
t += makerec(fout,23,"123456789");
t += makerec(fout,37,"abcdefghi");
t += makerec(fout,17,"jklmnopqr");
if (fseek(fout,0,0) != 0)
sysfault("makefile: unable to rewind -- %s\n",strerror(errno));
// write out real count
xwrite(&t,sizeof(t),fout);
fclose(fout);
}
void
showfile(const char *fileName5)
{
FILE *fin;
int t;
struct Record tmp;
fin = fopen(fileName5, "rb");
if (fin == NULL)
sysfault("showfile: Error opening %s -- %s\n",
fileName5,strerror(errno));
if (fread(&t, 1, sizeof(int), fin) != sizeof(int))
sysfault("deleteRec: error reading total -- %s\n",strerror(errno));
printf("showfile: count is %d\n",t);
while (fread(&tmp, 1, sizeof(tmp), fin) != 0)
printf("%d %s\n", tmp.data, tmp.studentID);
fclose(fin);
}
void
deleteRec(const char *fileName5,const char *studentID)
{
char tmpFile[1000];
FILE *file;
FILE *tmpf;
int erFlag = 0;
int foFlag = 0;
int t;
struct Record tmp;
printf("\n");
file = fopen(fileName5, "rb");
if (file == NULL)
sysfault("deleteRec: Error opening %s -- %s\n",
fileName5,strerror(errno));
if (fread(&t, 1, sizeof(int), file) != sizeof(int))
sysfault("deleteRec: error reading total -- %s\n",strerror(errno));
strcpy(tmpFile,fileName5);
strcat(tmpFile,".TMP");
tmpf = fopen(tmpFile, "wb");
if (tmpf == NULL)
sysfault("deleteRec: Error opening temp file -- %s\n",strerror(errno));
xwrite(&t, sizeof(int), tmpf);
while (fread(&tmp, 1, sizeof(tmp), file) != 0) {
printf("deleteRec: %d %s\n", tmp.data, tmp.studentID);
if (strcmp(tmp.studentID, studentID) != 0) {
xwrite(&tmp, sizeof(tmp), tmpf);
}
else {
printf("deleteRec: skipped %s\n", tmp.studentID);
foFlag = 1;
}
}
printf("closing file:%d\n", fclose(file));
printf("closing tmp: %d\n", fclose(tmpf));
if (foFlag == 0) {
printf("Record not found.\n");
remove(tmpFile);
}
else {
printf("in final stage.\n");
//printf("removing old file:%d\n", remove(fileName5));
printf("renaming file: %d\n", rename(tmpFile, fileName5));
}
}
void
deleteData(void)
{
char studentID[1000];
char fileName5[1000];
printf(" Enter file in which record is located:\n");
scanf("%s", fileName5);
printf("Enter student ID you want to delete:");
scanf("%s", studentID);
deleteRec(fileName5,studentID);
}
int
main(void)
{
const char *file = "mydata.bin";
makefile(file);
showfile(file);
deleteRec(file,"abcdefghi");
deleteRec(file,"zzzz");
showfile(file);
}
Here is the program output:
showfile: count is 3
23 123456789
37 abcdefghi
17 jklmnopqr
deleteRec: 23 123456789
deleteRec: 37 abcdefghi
deleteRec: skipped abcdefghi
deleteRec: 17 jklmnopqr
closing file:0
closing tmp: 0
in final stage.
renaming file: 0
deleteRec: 23 123456789
deleteRec: 17 jklmnopqr
closing file:0
closing tmp: 0
Record not found.
showfile: count is 3
23 123456789
17 jklmnopqr
Note that the count remains 3 because deleteRec does not adjust it down if it finds a record to delete [exercise left for reader ;-)]
If the file is unreadable, then the final showfile will error out.
Inspired by Neil's advice, I redefined the function so it reads the data into a struct array then write it (while omitting the record to be deleted) into the same file.
It now works, please see below:
void deleteData()
{
char studentID[10];
char fileName5[30];
FILE *file;
int foFlag = 0;
int total = 0;
struct Record *fileData5;
int j = 0;
do
{
printf(" Enter file in which record is located:\n");
scanf("%30s", fileName5);
if (!valSuf(fileName5))
printf("Error wih file name.\n");
} while (!valSuf(fileName5));
printf("Enter student ID you want to delete:");
scanf("%10s", studentID);
fileData5 = read_file(fileName5, &total);
if (fileData5 == false)
{
printf("Error reading file. \n");
}
struct Record *copy;
copy = malloc(sizeof(Record) * total);
for (int i = 0; i < total; i++)
{
if (strcmp(fileData5[i].studentId, studentID) != 0)
{
copy[j] = fileData5[i];
j++;
foFlag=0;
}
else
{
foFlag = 1;
}
}
if (foFlag == 0)
printf("Record not found.\n");
else if (writeFile(fileName5, copy, j))
printf("Record %s has been deleted successfully\n", studentID);
else
{
printf("Error deleting %s.\n", studentID);
}
free(copy);
}
I'm making a program that basically receives 2 files (a .pal and a .dic file) and outputs a file called solution file (blabla.sol). However that solution file's name depends on one of those files that the program received. How can I modify my program in order to change the output file's name depending on the name of the received file's name? In this case I want it to be the same names as my name_pal file.
Here's the program that I've already written (right now, as it is, it only outputs a file with a fixed name):
int main(int argc, char *argv[])
{
char name_dic[MAX_FILENAME_SIZE];
char name_pal[MAX_FILENAME_SIZE];
char name_sol[] = "prob0.sol";
char words_dic[MAX_WORD_SIZE];
char words_pal[MAX_WORD_SIZE];
char word_same_size[MAX_WORD_SIZE];
char dic_total[MAX_DIC_SIZE][MAX_WORD_SIZE];
char pal_total[MAX_PAL_SIZE][MAX_WORD_SIZE];
FILE *dic, *pal, *sol;
if (argc < 3)
{
printf("Missing files.\n");
exit(1);
}
strcpy(name_dic, argv[1]);
strcpy(name_pal, argv[2]);
dic = fopen(name_dic, "r");
if (dic == NULL)
{
printf("Can not open dictionary file: %s\n", name_dic);
exit(1);
}
pal = fopen(name_pal, "r");
if (pal == NULL)
{
printf("Can not open problem file: %s\n", name_pal);
exit(1);
}
sol = fopen(name_sol, "w");
if (sol == NULL)
{
printf("Can not open solution file: %s\n", name_sol);
exit(1);
}
/* rest of the code */
You can write a truncate function at the '.' and then append sol as shown in snippet below.
char name_sol[MAX_FILENAME_SIZE];
int i;
for(i=0;i<MAX_FILENAME_SIZE;i++){
name_sol[i]=name_pal[i];
if(name_sol[i]=='.') break;
}
strcpy(name_sol+i+1,"sol");
printf("%s\n",name_sol);
I have created a function that takes as a parameter the name of a source file, the name of a destination file and the beginning and end lines of the source file lines that will be copied to the destination file, like the example below. All I want to do is to input the lines that I want to copy to the other text file like the example below:
The code I show you just "reads" the content of the one text file and "writes" another one. I want to "write" specific lines that the user gives, not the whole text file
Inputs by the user:
Source_file.txt //the file that the destination file will read from
destination_file.txt //the new file that the program has written
2 3 // the lines that it will print to the destination file: 2-3
Source_file.txt:
1
2
3
4
5
6
destination_file.txt
2
3
code:
#include <stdio.h>
#include <stdlib.h>
void cp(char source_file[], char destination_file[], int lines_copy) {
char ch;
FILE *source, *destination;
source = fopen(source_file, "r");
if (source == NULL) {
printf("File name not found, make sure the source file exists and is ending at .txt\n");
exit(EXIT_FAILURE);
}
destination = fopen(destination_file, "w");
if (destination == NULL) {
fclose(source);
printf("Press any key to exit...\n");
exit(EXIT_FAILURE);
}
while ((ch = fgetc(source)) != EOF)
fputc(ch, destination);
printf("Copied lines %d from %s to %s \n",
lines_copy, source_file, destination_file, ".txt");
fclose(source);
fclose(destination);
}
int main() {
char s[20];
char d[20];
int lines;
printf("-Enter the name of the source file ending in .txt\n"
"-Enter the name of the destination file ending in .txt\n"
"-Enter the number of lines you want to copy\n\n");
printf(">subcopy.o ");
gets(s);
printf("destination file-> ");
gets(d);
printf("Lines: ");
scanf("%d", &lines);
cp(s, d, lines);
return 0;
}
In cp(), in order to select the lines to keep, you have to know their position in the input-file. Thus, you need to count lines.
Using fgets instead of fgetc will allow you to count the lines.
On the other hand, if I wanted to select lines 3 and 7 to 12 in a file, I'd use:
sed -n -e "3p;7,12p" < input.txt > output.txt
this is a very simple solution, let's say you know that the maximun length of a line will be 100 characters for simplicity (if a line is longer than 100 characters only the first 100 will be taken)
at the top (outside main) you can write
#ifndef MAX_LINE_SIZE
#define MAX_LINE_SIZE 100
#endif
i know many people don't like this but i think in this case it makes the code more elegant and easier to change if you need to modify the maximum line size.
to print only the wanted lines you can do something like this
char line[MAX_LINE_SIZE];
int count = 0;
while (fgets(line, MAX_LINE_SIZE, source)){
count++;
if (3 <= count && count <= 5){
fputs(line, destination);
}
}
The while loop will end when EOF is reched because fgets returns NULL.
P.S. there could be some slight errors here and there since i wrote it pretty fast and going by memory but in general it should work.
There are some problems in your program:
Do not use gets(), it may cause buffer overflows.
Always use type int to store the return value of fgetc() in order to distinguish EOF from regular byte values.
You pass an extra argument ".txt" to printf(). It will be ignored but should be removed nonetheless.
To copy a range of lines from source to destination, you can just modify your function this way:
#include <stdio.h>
#include <string.h>
#include <errno.h>
void cp(char source_file[], char destination_file[], int start_line, int end_line) {
int ch;
int line = 1, lines_copied;
FILE *source, *destination;
source = fopen(source_file, "r");
if (source == NULL) {
printf("Cannot open input file %s: %s\n",
source_file, strerror(errno));
exit(EXIT_FAILURE);
}
destination = fopen(destination_file, "w");
if (destination == NULL) {
printf("Cannot open output file %s: %s\n",
destination_file, strerror(errno));
fclose(source);
exit(EXIT_FAILURE);
}
while ((ch = fgetc(source)) != EOF) {
if (line >= start_line && line <= end_line) {
fputc(ch, destination);
}
if (ch == '\n') {
line++;
}
}
lines_copied = 0;
if (line > start_line) {
if (line >= end_line) {
lines_copied = end_line - start_line + 1;
} else {
lines_copied = line - start_line + 1;
}
}
printf("Copied lines %d from %s to %s\n",
lines_copy, source_file, destination_file);
fclose(source);
fclose(destination);
}
int main() {
char source_file[80];
char destination_file[80];
int start_line, end_line;
printf("-Enter the name of the source file ending in .txt\n"
"-Enter the name of the destination file ending in .txt\n"
"-Enter the start and end line\n\n");
printf(">subcopy.o ");
if (scanf("%79s", source_file) != 1) {
return 1;
}
printf("destination file-> ");
if (scanf("%79s", destination_file) != 1) {
return 1;
}
printf("Start and end lines: ");
if (scanf("%d %d", &start_line, &end_line) != 2) {
return 1;
}
cp(source_file, destination_file, start_line, end_line);
return 0;
}
I want to take input from a file in c++. I am using Visual studio 2015. My sample code function for taking input from file is:
#define INPUT_FILE_NAME "input.txt"
#define V 15
int total number=0;
double px[V];
double py[V];
void fileInput()
{
FILE *fp;
char cwd[1024];
if (getcwd(cwd, sizeof(cwd)) == NULL) //Directory not found
{
perror("getcwd() error - Current directory not found !!");
exit(0);
}
strcat(cwd, "\\");
strcat(cwd, INPUT_FILE_NAME);
fp = fopen(cwd, "r");
int i = 0;
fscanf(fp, "%d", &start);
while (fscanf(fp, "%lf", &px[i]) != EOF)
{
fscanf(fp, "%lf", &py[i]);
i++;
total_number++;
}
}
My input file format is as follows:
2
0 1
2 1
But when I compile, I get an error like: identifier 'getcwd' is undefined. Is there any way to replace this function, so that I can get the same output?
try the header #include "direct.h" it might be why...
First off I am creating a program that will read lines of characters and find words (they don't have to have meaning, i.e 'ab' could be word ) and storing them in the appropriate data structure. I used trie structure to store the words. I am given a mapping file as a command line argument yet inside the mapping file I have two data files I need to gain information from. The usage interface is as follows: first(program name) <mappingfile>.
Inside the mapping file, there exists two data files: <dictFile> and <dataFile>. Im not sure how to read and store the information presented the two data files. So far I have the following:
#include <stdio.h>
#include<stdlib.h>
void readDict(FILE *dict_file){
}
int main(int argc, char *argv[]){
FILE* file;
if(argc != 2){ //error in inputing, not 2 files
printf("error\n");
return 0;
}
file = fopen(argv[1],"r" ); //reading the mapping file
input;
if(file == NULL){ //nothing inside file
printf("file does not exist\n");
return 0;
}
}
My goal is to have pointers point to respective data files in the mapping file which I can use for reading their contents.
I will be given the following input in the command line:
first(program name) <mappingfile>.
Inisde the mapping file contains the lines of two plain .txt files in the form
<dictFile> <dataFile>.
I wish to access both contents of <dictFile> and <dataFile>.. with pointers to the respective file.
If I understand your question correctly you want to parse a file where each line contains the filenames of two other files and then read from these. What you can do is use fgets to read your mapping file line by line. What you can do next is use the function strtok to split your string on a whitespace. I'll break it down for you step by step.
Firstly we want to open the mapping file for reading
if((file = fopen(argv[1],"r")) == NULL) {
perror("error opening file");
return 1;
}
This will try to open the mapping file specified by the command line arguments of your program and if it fails it will print a corresponding error message.
while(fgets(buf, sizeof(buf), file) != NULL) {
After we've opened the file we want to iterate through all the lines until we reach the end of the file and fgets will return NULL. fgets will put the current line into buf.
dictfilename = strtok(buf, " ");
datafilename = strtok(NULL, " ");
strtok(dictfilename, "\n"); /* Remove any trailing newlines */
strtok(datafilename, "\n");
We need to split the line read by fgets by a delimter (a whitespace) so we know which part corresponds to the dictfile and the datafile. This is done by using the strtok function which returns a pointer to the substring before the whitespace and when passing in NULL it will return a pointer to the substring after the whitespace. A slightly weird way of removing any trailing newlines is to use strtok and the newline as a delimiter.
if((dictfile = fopen(dictfilename,"r")) == NULL) {
fprintf(stderr, "error opening file %s: %s\n", dictfilename, strerror(errno));
return 1;
}
if((datafile = fopen(datafilename,"r")) == NULL) {
fprintf(stderr, "error opening file %s: %s\n", datafilename, strerror(errno));
return 1;
}
Very similiarly to how we open the mapping file, we now open the two files found on the current line read by fgets with "r" mode which opens for reading. If the file does not exist or cannot be found, the fopen call fails.
printf("Content of %s:\n", dictfilename);
while ((c = getc(dictfile)) != EOF)
putchar(c);
printf("\nContent of %s:\n", datafilename);
while ((c = getc(datafile)) != EOF)
putchar(c);
This is a very simple method of "dumping" the content of the files. It uses getc to read the next char from the file and prints it until it reads EOF. This is where you should do your own function.
fclose(dictfile);
fclose(datafile);
And don't forget to close the files afterwards or you will leak resources.
Finally here is the code on what I just described
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#define MAX_LENGTH 100 // change this to the actual maximum length of your lines.
int main(int argc, char **argv){
FILE* file, *dictfile, *datafile;
char c;
char buf[MAX_LENGTH];
char *dictfilename, *datafilename;
if(argc != 2) {
fprintf(stderr, "Usage: %s <mapping file>\n", argv[0]);
return 0;
}
if((file = fopen(argv[1],"r")) == NULL) {
perror("error opening file");
return 1;
}
while(fgets(buf, sizeof(buf), file) != NULL) {
dictfilename = strtok(buf, " ");
datafilename = strtok(NULL, " ");
strtok(dictfilename, "\n"); /* Remove any trailing newlines */
strtok(datafilename, "\n");
if((dictfile = fopen(dictfilename,"r")) == NULL) {
fprintf(stderr, "error opening file %s: %s\n", dictfilename, strerror(errno));
return 1;
}
if((datafile = fopen(datafilename,"r")) == NULL) {
fprintf(stderr, "error opening file %s: %s\n", datafilename, strerror(errno));
return 1;
}
// do something with the files (e.g read all the content)
printf("Content of %s:\n", dictfilename);
while ((c = getc(dictfile)) != EOF)
putchar(c);
printf("\nContent of %s:\n", datafilename);
while ((c = getc(datafile)) != EOF)
putchar(c);
printf("\n");
// don't forget to close the files when you're done with them.
fclose(dictfile);
fclose(datafile);
}
fclose(file);
}
If I understand you correctly this should do it. Note that it assumes your filenames don't have any spaces. And if you want to use the "non secure" api's you need to add _CRT_SECURE_NO_WARNINGS to the project properties under Configuration Properties -> C/C++ -> Preprocessor -> Preprocessor Definitions.
#include <stdio.h>
#include<stdlib.h>
void readDict(FILE *dict_file){
}
int main(int argc, char *argv[]){
FILE* file;
if(argc != 2){ //error in inputing, not 2 files
printf("error\n");
return 1;
}
file = fopen(argv[1],"r" ); //reading the mapping file
//input;
if(file == NULL){ //nothing inside file
printf("file does not exist\n");
return 1;
}
char dictFileString[256], dataFileString[256];
fscanf( file, "%255s %255s", dictFileString, dataFileString );
FILE *dictFile, *dataFile;
dictFile = fopen( dictFileString, "r" );
if (dictFile == NULL) {
printf( "%s does not exist\n", dictFileString );
fclose(file);
return 1;
}
dataFile = fopen( dataFileString, "r" );
if (dataFile == NULL) {
printf( "%s does not exist\n", dataFileString );
fclose(file);
fclose(dictFile);
return 1;
}
readDict(dictFile);
// The additional logic would be placed here.
fclose( dictFile );
fclose( dataFile );
// If you need to read additional file names then loop
// back up to read the next line of 'file'
fclose( file );
return 0;
}