I hope I formatted the code correctly this time. Let me say first that the code works as is; it's in understanding some parts and modifying others that I run into trouble.
I'm going to delete my numerous comments and limit myself to a few questions on it.
1. Is FILE a keyword in Obj-C? What is its function? Why all caps?
2. What does "r" do?
3. The text file already has strings containing empty spaces, each ending with \n; why not make them NSStrings instead of c-strings?
4. Why, when I try to change the launch parameter of the file (using executables, clicking on arguments and plus, and typing in the parameter) to anything other than /tmp (such as /Desktop), do I get errors? After all, /tmp is a volatile, vulnerable place. This is the error I got for /Desktop: The Debugger has exited due to signal 10 (SIGBUS).
Here's the code:
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
if (argc == 1)
{
NSLog(#"You need to provide a file name");
return 1;
}
FILE *wordFile = fopen(argv[1], "r");
char word[100];
while (fgets (word, 100, wordFile))
{
word[strlen(word) - 1] = '\0';
NSLog(#"%s is %d characs long", word, strlen(word));
}
fclose (wordFile);
[pool drain];
return 0;
}
Most of this is standard C stuff, it happens to be compiled as objective-c but FILE and fopen() and fgets() are plain old fashioned C.
FILE is presumbably a #define'd somewhere to refer to a structure definition. It is not a keyword, just a regular symbol defined (I think) in stdio.h.
"r" means "readable". Look up fopen for all the values that argument can have, but "r", "r+", "b", "a", "w" etc are some of the options.
Are you sure /Desktop is a valid directory? Change to that directory in a console window and type "pwd" to make sure youve got the right path. You might want to have an error message if wordFile is null (i.e. couldn't find the file or open it for some reason) before trying to use fgets on it.
Related
This question already has answers here:
Using a variable file name in C to read from multiple files with similar names?
(2 answers)
Closed 7 years ago.
Using Visual Studio 2015 how would i open and read all the file in a directory.
The Input Parameters for the program are
Number of Sensors (N): Determines the number of input files
File Location: A local directory/folder where the files are located. Each file will be named: sensor_0.txt, sensor_1.txt, ... sensor_(n - 1).txt
I can open and read individual files in the directory by hard coding them using fopen, but since the number of input files is not constant I don't know how I would read all of the files in the directory regardless of how many input files there are.
I was thinking that i would need to create the file names since the only thing changing in the file names is the sensor number but that doesn't seem to work since fopen requires a const char * file name.
I have searched for solutions and i found a DIR variable type in dirent.h header file, but that doesn't work with the the Visual Studio Compiler and a package needs to be installed in order to use that header file.
I am in an intro to programming class so i feel like installing outside programs would be the wrong approach to solving this issue, but I could be wrong. I have also looked into functions like FindFirstFile, and FindNextFile but those also seem too advanced for me.
Any help would be really would be appreciated. Thank you in advance.
If you're writing a Windows-specific application (rather than something that needs to be portable to other operating systems) then look into the FindFirstFile, FindNextFile, and FindClose APIs.
Here's a sample of how to use these APIs (based somewhat on the samples from the above links):
#include <windows.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
WIN32_FIND_DATA FindFileData;
HANDLE hFind;
if (argc != 2) {
printf("Usage: %s [target_file]\n", argv[0]);
return 1;
}
printf("Target file is %s\n", argv[1]);
hFind = FindFirstFile(argv[1], &FindFileData);
if (hFind == INVALID_HANDLE_VALUE) {
printf("FindFirstFile failed, error %d\n", GetLastError());
return 1;
}
do {
printf("File name = %s\n", FileFindData.cFileName);
} while (FindNextFile(hFind, &FindFileData));
FindClose(hFind);
return 0;
}
Disclaimer: I haven't had a Windows dev environment years, so I have no way to compile & verify this sample. It should get you pointed in the right direction, though.
You can just do it by hardcoding the base name and iterating with an index to generate the specific name, something like this
for (size_t i = 0 ; ; ++i)
{
char filepath[MAX_PATH];
FILE *file;
// In principle, you should check the return value to ensure
// it didn't truncate the name
snprintf(filepath, sizeof(filepath), "sensor_%d.txt", i);
// Try to open the file, if it fails it's probably because
// the file did not exist, but it's not the only possible
// reason.
file = fopen(filepath, "r"); // Or "rb", depends ...
if ((done = (file == NULL)) != 0)
break; // Cannot open this, probably there are no more files.
// Process the file here
}
A better way would be to pass the name to another function, so you can later change the name generation method by looking at the directory instead of assuming it.
NOTE 1: Secure c Runtime, in MSVC compiler will probably complain about fopen() and snprintf() since snprintf() uses the POSIX name style or something like that (perhaps using the safe version snprintf_s()) I don't remember. But this is standard c (as per C11) so it should compile with any c compiler.
NOTE 2: You should also, use the full path unless the files are in the CWD. Something like (assuming the files are in drive "C:")
snprintf(filepath, sizeof(filepath), "C:\\full\\path\\sensor_%d.txt", i);
I am trying to make a function in C to erase all the contents of a temp folder and to erase the folder.
Whilst I already have successfully created the code to cycle through the files and to erase the folder (it is pretty much straight forward) I am having trouble erasing the files using unlink.
Here is the code that I am using:
int delete_folder(char *foldername) {
DIR *dp;
struct dirent *ep;
dp=opendir(foldername);
if (dp!=NULL) {
readdir(dp); readdir(dp);
while (ep=readdir(dp)) {
char* cell = concatenate(concatenate(foldername, "\\"), "Bayesian Estimation.xlsx");//ep->d_name);
printf("%s\n", cell);
remove(cell);
printf("%s\n", strerror(errno));
}
closedir(dp);
}
if (!rmdir(foldername)) {return(0);} else {return(-1);}
}
The code that I wrote is fully functional for all files but those which include spaces in the filename. After some testing, I can guarantee that the unlink functions eliminates all files in the folder (even those with special characters in the filename) but fails if the filename includes a space (however, for this same file, if I remove the space(s), this function works again).
Has anyone else encountered this problem? And, more importantly, can it be solved/circunvented?
(The problem remains even if I introduce the space escape sequences directly)
The error presented by unlink is "No such file or directory" (ENOENT). Mind you that the file is indeed at the referred location (as can be verified by the code outputing the correct filename in the variable cell) and this error also occurs if I use the function remove instead of unlink.
PS: The function concatenate is a function of my own making which outputs the concatenation of the two input strings.
Edit:
The code was written in Codeblocks, in Windows.
Here's the code for the concatenate function:
char* concatenate(char *str1, char *str2) {
int a1 = strlen(str1), a2 = strlen(str2); char* str3[a1+a2+1];
snprintf(str3, a1+a2+2, "%s%s", str1, str2);
return(str3);
}
Whilst you are right in saying that it is a possible (and easy) memory leak, the functions' inputs and outputs are code generated and only for personal use and therefore there is no great reason to worry about it (no real need for foolproofing the code.)
You say "using unlink()" but the code is using remove(). Which platform are you on? Is there any danger that your platform implements remove() by running an external command which doesn't handle spaces in file names properly? On most systems, that won't be a problem.
What is a problem is that you don't check the return value from remove() before printing the error. You should only print the error if the function indicates that it generated an error. No function in the Standard C (or POSIX) library sets errno to zero. Also, errors should be reported on standard error; that's what the standard error stream is for.
if (remove(cell) != 0)
fprintf(stderr, "Failed to remove %s (%d: %s)\n", cell, errno, strerror(errno));
else
printf("%s removed OK\n", cell);
I regard the else clause as a temporary measure while you're getting the code working.
It also looks like you're leaking memory like a proverbial sieve. You capture the result of a double concatenate operation in cell, but you never free it. Indeed, if the nested calls both allocate memory, then you've got a leak even if you add free(cell); at the end of the loop (inside the loop, after the second printf(), the one I deconstructed). If concatenate() doesn't allocate new memory each time (it returns a pointer to statically allocated memory, then I think concatenating a string with the output of concatenate() is also dangerous, probably invoking undefined behaviour as you copy a string over itself. You need to look hard at the code for concatenate(), and/or present it for analyis.
Thank you very much for all your input, after reviewing your comments and making a few experiments myself, I figured out that remove/unlink was not working because the filename was only temporarily saved at variable cell (it was there long enough for it to be printed correctly to console, hence my confusion). After appropriately storing my filename before usage, my problem has been completely solved.
Here's the code (I have already checked it with filenames as complex as I could make them):
int delete_folder(char* foldername) {
DIR *dp;
struct dirent *ep;
dp=opendir(foldername);
if (dp!=NULL) {
readdir(dp); readdir(dp);
while (ep=readdir(dp)) {
char cell[strlen(foldername)+1+strlen(ep->d_name)+1];
strcpy(cell, concatenate(concatenate(foldername, "\\"), ep->d_name));
unlink(cell);
printf("File \"%s\": %s\n", ep->d_name, strerror(errno));
}
closedir(dp);
}
if (!rmdir(foldername)) {return(0);} else {return(-1);}
}
I realize it was kind of a noob mistake, resulting from my being a bit out of practice for a while in programming in C, so... Thank you very much for your all your help!
Do any of you guys know if it's possible to update a text file(e.g something.txt) in C?
I was expecting to find a function with similar syntax as update_txt(something.txt), but I haven't found anything while browsing the internet for the last 2 hours.....
The thing is that I would like some data to be stored and displayed in real time in an already opened text file. I can store the data but I am unable to find a way to display it without manually closing the text file and then open it again...
Do someone know how to solve this issue? Or do you have another way to solve it? I have read something about transferring data to a new text document and then renaming it, but I am quite sure that this wouldn't solve my problem. I have also read something about macros that could detect changes in the document and then somehow refresh it. I have never worked with macros and I have absolutely no idea of how they are implemented....
But please tell me if it is a fact that it is impossible to update an already opened text document?
I am thankful for any suggestions or tutorials that you guys may provide! :)
That's outside the scope of C; it will require some system-specific filesystem monitoring mechanism. For example, inotify offers this functionality
First off, you can use the rewind(), fseek(), ftell() or fgetpos() and fsetpos() functions to locate the read pointer in a file. If you record the start position where the updated record was written (the start offset) using ftell() or fgetpos(), you could jump back to that position later with fseek() or fsetpos() and read in the changed data.
The other gotcha lurking here is that in general, you can't simply 'update' a text file. Specifically, if the replacement text is not the same length as the original text, you have problems. You either need to expand or contract the file. This is normally done by making a copy with the desired edit in the correct location, and then copying or moving the modified copy of the file over the original file.
Detecting when some other process modifies the file is harder still. There are different mechanisms in different operating systems. For Linux, it is the inotify system, for example.
Based upon your statement that you 'can't display it without manually closing the text file and open it again', it may be a buffer issue. When using the C standard library calls (fopen, fread, fwrite, fclose, etc ...) the data you write may be buffered in user-space until the buffer is full or the file is closed.
To force the C library to flush the buffer, use the fflush(fp) call where fp is your file pointer.
Regarding: But please tell me if it is a fact that it is impossible to update an already opened text document? Yes, it is not possible, unless you own the handle to the file (i.e. FILE *fp = fopen("someFilePath", "w+");)
Regarding: if it's possible to update a text file(e.g something.txt) in C?
Yes. If you know the location of the file, (someFileLocation, eg. "c:\dev\somefile.txt"), then open it and write to it.
A simple function that uses FILE *fp = fopen(someFileLocation, "w+"); (open existing file for append) and fclose(fp); will do that: Here is an example that I use for logging:
(Note, you will have to comment out, or create the other functions this one refers to, but the general concept is shown)
int WriteToLog(char* str)
{
FILE* log;
char *tmStr;
ssize_t size;
char pn[MAX_PATHNAME_LEN];
char path[MAX_PATHNAME_LEN], base[50], ext[5];
char LocationKeep[MAX_PATHNAME_LEN];
static unsigned long long index = 0;
if(str)
{
if(FileExists(LOGFILE, &size))
{
strcpy(pn,LOGFILE);
ManageLogs(pn, LOGSIZE);
tmStr = calloc(25, sizeof(char));
log = fopen(LOGFILE, "a+");
if (log == NULL)
{
free(tmStr);
return -1;
}
//fprintf(log, "%10llu %s: %s - %d\n", index++, GetTimeString(tmStr), str, GetClockCycles());
fprintf(log, "%s: %s - %d\n", GetTimeString(tmStr), str, GetClockCycles());
//fprintf(log, "%s: %s\n", GetTimeString(tmStr), str);
fclose(log);
free(tmStr);
}
else
{
strcpy(LocationKeep, LOGFILE);
GetFileParts(LocationKeep, path, base, ext);
CheckAndOrCreateDirectories(path);
tmStr = calloc(25, sizeof(char));
log = fopen(LOGFILE, "a+");
if (log == NULL)
{
free(tmStr);
return -1;
}
fprintf(log, "%s: %s - %d\n", GetTimeString(tmStr), str, GetClockCycles());
//fprintf(log, "%s: %s\n", GetTimeString(tmStr), str);
fclose(log);
free(tmStr);
}
}
return 0;
}
Regarding: browsing the internet for the last 2 hours. Next time try
"tutorial on writing to a file in C" in Google, it lists lots of links, including:
This one... More On The Topic.
I am using fopen(3) in C to read file and process it. The file is present in current working directory where the binary exists, but I am unable to read the file (Linux environment / Cygwin environment).
Here is the sample code:
C code:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
FILE *inFile;
static char fileName[255];
int process_file(FILE *inFile)
{
char ch;
inFile = fopen(fileName,"r");
if (inFile == NULL)
{
perror(fileName);
exit(1);
}
else
{
// Process file
}
fclose(inFile);
return 0;
}
int main(int argc, char *argv[])
{
printf("Enter filename to process \n");
scanf("%s", fileName);
process_file(inFile);
getchar();
return 0;
}
I have file permissions set to 777 in the current directory. The resulting binary as well as my source code reside in this directory where the input file exits. Why is the file not opened?
Update :
This question was written in few years back and this code could be improved a lot.
1. The process file should accept char * or char array instead of file pointer
2. unused variables can be removed
3. unused libraries or include files can be removed
4. Can make use of argv to accept filename with path from cmdline
5. return instead of exit in process_file and also proper return code instead of returning 0 from process_file.
I should have asked this question little more elaborate...
I had three functions to process the same file, like process_fil1e1(), process_file2() and process_file3() even though I called fclose() in all three functions. Somehow the file handle was not closed that properly or the file pointer pointed to EOF or some undefined behavior. It was not working fine.
When I used a single process file and rewind() together, it worked fine...
Be sure to input file name with its extension. This may cause problems with reading the file.
If you know the extension of the file you can input only the name and after that make the program add the extension. After scanf("%s", fileName); add strcat(fileName, ".txt"); if you want to enter only the name without extension and the file you read has extension .txt.
Your inFile and fileName variables are extern so you don't need to have arguments for the function process_file();, any function can access those variables.
You can change function int process_file(); to void process_file(); and delete return 0, you don't need that.
You have declared the inFile and fileName as global. You should change your function prototype from
int process_file(FILE *inFile)
to
int process_file()
This would at least make your program more clear. Now regarding your problem: It would almost certain be that you are doing something wrong in the input file (like not putting in the file extension) in your input. Remember, you need to pass the complete file name (including the extension which on some systems like Windows (by default) would be hidden). Otherwise, the logic looks correct to me, and it should work fine.
I am trying to create a a program that does the following actions:
Open a file and read one line.
Open another file and read another line.
Compare the two lines and print a message.
This is my code:
#include <stdio.h>
#include <string.h>
int findWord(char sizeLineInput2[512]);
int main()
{
FILE*cfPtr2,*cfPtr1;
int i;
char sizeLineInput1[512],sizeLineInput2[512];
cfPtr2=fopen("mike2.txt","r");
// I open the first file
while (fgets(sizeLineInput2, 512, cfPtr2)!=NULL)
// I read from the first 1 file one line
{
if (sizeLineInput2[strlen(sizeLineInput2)-1]=='\n')
sizeLineInput2[strlen(sizeLineInput2)-1]='\0';
printf("%s \n",sizeLineInput2);
i=findWord(sizeLineInput2);
//I call the procedure that compares the two lines
}
getchar();
return 0;
}
int findWord(char sizeLineInput2[512])
{
int x;
char sizeLineInput1[512];
File *cfPtr1;
cfPtr1=fopen("mike1.txt","r");
// here I open the second file
while (fgets(sizeLineInput1, 512,cfPtr1)!=NULL)
{
if (sizeLineInput1[strlen(sizeLineInput1)-1]=='\n')
sizeLineInput1[strlen(sizeLineInput1)-1]='\0';
if (strcmp(sizeLineInput1,sizeLineInput2)==0)
//Here, I compare the two lines
printf("the words %s and %s are equal!\n",sizeLineInput1,sizeLineInput2);
else
printf("the words %s and %s are not equal!\n",sizeLineInput1,sizeLineInput2);
}
fclose(cfPtr1);
return 0;
}
It seems to have some problem with file pointers handling. Could someone check it and tell me what corrections I have to do?
Deconstruction and Reconstruction
The current code structure is, to be polite about it, cock-eyed.
You should open the files in the same function - probably main(). There should be two parallel blocks of code. In fact, ideally, you'd do your opening and error handling in a function so that main() simply contains:
FILE *cfPtr1 = file_open("mike1.txt");
FILE *cfPtr2 = file_open("mike2.txt");
If control returns to main(), the files are open, ready for use.
You then need to read a line from each file - in main() again. If either file does not contain a line, then you can bail out with an appropriate error:
if (fgets(buffer1, sizeof(buffer1), cfPtr1) == 0)
...error: failed to read file1...
if (fgets(buffer2, sizeof(buffer2), cfPtr2) == 0)
...error: failed to read file2...
Then you call you comparison code with the two lines:
findWord(buffer1, buffer2);
You need to carefully segregate the I/O operations from the actual processing of data; if you interleave them as in your first attempt, it makes everything very messy. I/O tends to be messy, simply because you have error conditions to deal with - that's why I shunted the open operation into a separate function (doubly so since you need to do it twice).
You could decide to wrap the fgets() call and error handling up in a function, too:
const char *file1 = "mike1.txt";
const char *file2 = "mike2.txt";
read_line(cfPtr1, file1, buffer1, sizeof(buffer1));
read_line(cfPtr2, file2, buffer2, sizeof(buffer2));
That function can trim the newline off the end of the string and deal with anything else that you want it to do - and report an accurate error, including the file name, if anything goes wrong. Clearly, with the variables 'file1' and 'file2' on hand, you'd use those instead of literal strings in the file_open() calls. Note, too, that making them into variables means it is trivial to take the file names from the command line; you simply set 'file1' and 'file2' to point to the argument list instead of the hard-wired defaults. (I actually wrote: const char file1[] = "mike1.txt"; briefly - but then realized that if you handle the file names via the command line, then you need pointers, not arrays.)
Also, if you open a file, you should close the file too. Granted, if your program exits, the o/s cleans up behind you, but it is a good discipline to get into. One reason is that not every program exits (think of the daemons running services on your computer). Another is that you quite often use a resource (file, in the current discussion) briefly and do not need it again. You should not hold resources in your program for longer than you need them.
Philosophy
Polya, in his 1957 book "How To Solve It", has a dictum:
Try to treat symmetrically what is symmetrical, and do not destroy wantonly any natural symmetry.
That is as valid advice in programming as it is in mathematics. And in their classic 1978 book 'The Elements of Programming Style', Kernighan and Plauger make the telling statements:
[The] subroutine call permits us to summarize the irregularities in the argument list [...]
The subroutine itself summarizes the regularities of the code.
In more modern books such as 'The Pragmatic Programmer' by Hunt & Thomas (1999), the dictum is translated into a snappy TLA:
DRY - Don't Repeat Yourself.
If you find your code doing the 'same' lines of code repeated several times, write a subroutine to do it once and call the subroutine several times.
That is what my suggested rewrite is aiming at.
In both main() and findWord() you should not use strlen(sizeLineInputX) right after reading the file with fgets() - there may be no '\0' in sizeLineInput2 and you will have strlen() read beyond the 512 bytes you have.
Instead of using fgets use fgetc to read char by char and check for a newline character (and for EOF too).
UPD to your UPD: you compare each line of mike2.txt with each line of mike1.txt - i guess that's not what you want. Open both files one outside while loop in main(), use one loop for both files and check for newline and EOF on both of them in that loop.