I want to write a code to extract todo task list from a code file.It's basically scanning a code file and detecting lines that include "TODO" string and then writing those lines into a text file.
So far my my code is like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
FILE* f;
char line[200];
f = fopen("someFile.c", "r");
char c;
char str;
while(!feof(f)){
fgets(line,sizeof(line),f);
if(strstr(line, "TODO") != NULL)//Extracts every line with TODO
{
c=fgetc(f);//c = lines with TODO
}
}
fclose(f);
f= fopen("todoListFile.txt","w");
while(!feof(f))
{
fputs(c,f);//Writing the content of the c in to the text file.
}
fclose(f);
return 0;
}
When I run this code it crashes after 1-2 seconds.
My mistake is probably at the second part which is getting those "TODO" lines and writing down those to the text lines. But I'm pretty stuck at that part and don't know what to do.
Note: Content of someFile.c is basically some comment lines with "// TODO :"
The specification pretty much indicates that you have to open two files, one for reading, one for writing. As you read a line from the input file, if that line contains TODO, you need to write that line to the output file. That leads to the straight-forward code:
#include <stdio.h>
#include <string.h>
int main(void)
{
char file1[] = "someFile.c";
char file2[] = "todoListFile.txt";
FILE *fp1 = fopen(file1, "r");
if (fp1 == NULL)
{
fprintf(stderr, "Failed to open file %s for reading\n", file1);
return 1;
}
FILE *fp2 = fopen(file2, "w");
if (fp2 == NULL)
{
fprintf(stderr, "Failed to open file %s for writing\n", file2);
return 1;
}
char line[200];
while (fgets(line, sizeof(line), fp1) != 0)
{
if (strstr(line, "TODO") != NULL)
fputs(line, fp2);
}
fclose(fp1);
fclose(fp2);
return 0;
}
Note that it checks that the files were opened successfully, and reports the file name if it failed, and exits with a non-zero status (you could add <stdlib.h> and use EXIT_FAILURE if you prefer).
When run on (a copy of) its own source, it leaves the todoListFile.txt containing one line:
if (strstr(line, "TODO") != NULL)
Simple modifications of the program would:
Write to standard output instead a fixed name file.
Take command line arguments and process all the input files named.
Read standard input if no input files are named.
Increase the line length. 200 is better than 80, but lines can be longer than that. I tend to use 4096 as a line length unless there's a reason to allow longer lines.
Related
my C program does 3 things:
reads from a text file
converts all letters into upper-case
prints the converted text into the console.
Here are how many times I'm opening and closing those 2 files in my program:
Original => 1 for "r"
New => 2 first for "w+" and last for "r"
Is there a better way to write to a file and read from it without opening and closing a multiple times? (even though I only opened and closed twice, I wanna build a good practice)
#include <stdio.h>
#include <ctype.h>
int main()
{
const char ORIGINAL[] = "challenge2.txt";
FILE *fp = NULL;
FILE *fpNew = NULL;
char ch, ch2;
///////////// open the original txt file to read /////////////
fp = fopen(ORIGINAL, "r");
if (fp == NULL)
{
perror("Error opening the file");
return (-1);
}
///////////// create and write on a new file /////////////
fpNew = fopen("challenge2_copy.txt", "w+");
printf("\n============== Original text ==============\n");
while ((ch = fgetc(fp)) != EOF)
{
printf("%c", ch);
ch = toupper(ch);
fputc(ch, fpNew);
}
fclose(fp);
fp = NULL;
fclose(fpNew);
fpNew = NULL;
///////////// call the new file to print the converted text /////////////
fpNew = fopen("challenge2_copy.txt", "r");
if (fpNew == NULL)
{
perror("Error opening the file");
return (-1);
}
printf("\n============== Converted to Uppercase ==============\n");
while ((ch2 = fgetc(fpNew)) != EOF)
{
printf("%c", ch2);
}
fclose(fpNew);
fpNew = NULL;
return 0;
}
Here's the console output:
============== Original text ==============
hello I AM JACK
I AM TESTING lowerCASE
GONNA convert THIS into
UPPERcase
i hope THIS works
============== Converted to Uppercase ==============
HELLO I AM JACK
I AM TESTING LOWERCASE
GONNA CONVERT THIS INTO
UPPERCASE
I HOPE THIS WORKS
Good practices, performance, dangers.
MS Visual Studio suggest using fopen_s as good practise :)
Sometimes reopening file sometimes makes code more clear to read in big projects.
As for performance, it will take some time for processor to make new FILE instance and fill it with all file properties.
There can also be some interrupts eg. after releasing ownership for a while cloud sync. tools may want to back up newly created file and will block accessing it for other apps. (your program).
Performance solution.
So as to reuse a FILE instance you need only to jump into different place in FILE buffer (eg. start of the file).
You can achieve it with fsetpos or fseek functions from stdio.h.
https://www.cplusplus.com/reference/cstdio/fsetpos/
https://www.cplusplus.com/reference/cstdio/fseek/
Example FILE instance reusage.
/* fsetpos example */
#include <stdio.h>
int main ()
{
FILE * pFile; fpos_t position;
#define buffSize 1024 //1KB
char s[buffSize];
//Write
pFile = fopen ("myfile.txt","w+");
fgetpos (pFile, &position);
fputs ("That is a sample",pFile);
//Reuse for reading
fsetpos (pFile, &position);
puts (fgets(s,buffSize, pFile));
//Next reuse for reading
fsetpos (pFile, &position);
puts (fgets(s,buffSize, pFile));
fclose (pFile);
return 0;
}
The above code produces the following result:
That is a sample
That is a sample
The code is running well, it's just that I feel there are still many mistakes and give me a little direction to improve in the future. I want to learn how to maintain the code properly.
fix the code as it should!
Data.txt
[1] Line numbers 1.
[2] Line numbers 2.
[3] Line numbers 3.
[4] Line numbers 4.
[5] Line numbers 5.
[6] Line numbers 6.
My code:
#include <stdio.h>
#include <stdlib.h>
int getLengthFile(char *namafile)
{
FILE *fptr;
int n =0;
fptr = fopen(namafile, "r");
if(fptr != NULL){
char c;
while((c = getc(fptr)) != EOF) {
++n;
}
fclose(fptr);
}
return n;
}
int main(){
FILE *fptr;
int i;
fptr = fopen("Data.txt","r");
if(fptr != NULL){
printf("Succes reads file!\n");
if(getLengthFile("Data.txt")>0){
char strLine[225];
while(fgets(strLine,225,fptr) != NULL){
printf("%s",strLine);
}
}else{
printf("File is empty!\n");
}
fclose(fptr);
}else{
printf("Error reads file!\n");
}
return 0;
}
Here is a more or less fixed version of the code presented in the first edition of the question, where the getLengthFile() function was not present. In my opinion, that function does not provide useful functionality. If you must report that the file contained no data, you could do so by counting the number of times fgets() returns any data — if it returns any data, the file was not empty.
#include <stdio.h>
int main(void)
{
const char filename[] = "Data.txt";
FILE *fptr = fopen(filename, "r");
if (fptr == NULL)
{
fprintf(stderr, "Error opening file %s for reading!\n", filename);
return 1;
}
printf("Success opening file %s for reading\n", filename);
char strLine[225];
while (fgets(strLine, sizeof(strLine), fptr) != NULL)
printf("%s", strLine);
fclose(fptr);
return 0;
}
When there is no file Data.txt, example output is:
Error opening file Data.txt for reading!
When there's a file containing one short line of data, example output is:
Success opening file Data.txt for reading
data from the file Data.txt
I also tested it on a file with longer lines, including lines with as many as 380 characters, and the output from the program was the same as the input except for the line saying 'Success opening file Data.txt for reading'.
I want to check if there are any duplicates in a .txt file. I've wrote a code but it's not running. I'm not sure about opening the norep.txt file in "a+" mode. The idea is to put the first word of my text in the norep.txt file, then compare every word in the text.txt with the words in norep.txt and copy only the words I need in the file.
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *fd;
FILE *ft;
char aux[30];
char aux1[30];
int len;
fd = fopen("c:\\text.txt", "r");
if (fd == NULL) {
puts("Error");
}
ft = fopen("c:\\norep.txt", "a+");
if (ft == NULL) {
puts("Error");
}
fscanf(fd, "%s", aux);
fprintf(ft, "%s", aux);
rewind(fd);
rewind(ft);
while (!feof(fd)) {
fscanf(fd, "%s", aux);
while (!feof(ft)) {
fscanf(ft, "%s", aux1);
len = strcmp(aux, aux1);
if (len != 0) {
fprintf(ft, "%s", aux);
}
}
rewind(ft);
}
return 0;
}
You should flush the output file before you rewind it.
fflush - flush a stream or fflush
Of course, this will not fix your problem because:
Note below that the manual says that reposition operations are ignored so that your attempt to read will always find the end of file.
append: Open file for output at the end of a file. Output operations
always write data at the end of the file, expanding it. Repositioning
operations (fseek, fsetpos, rewind) are ignored. The file is created
if it does not exist.
What you should probably do is create an internal memory table that keeps all the unique entries and write it out to a new file after all processing is done. As you read the fd file, check the list and add a new entry if it is not already in the list. Then after you have finished processing fd, then and only then write out your list. Of course, this may be too big depending on the size of your data file.
You could append each unique entry to the output file as you go. but you would need to have some method of checking the previous entries without trying to read the output file.
The usual way to go about this is to read the input file word for word, store the necessary information in some way and then, after you have read all information from the file, write the desired output to the output file.
A rough skeleton of that approach might look like this:
int main()
{
const char *infile = "text.txt";
const char *outfile = "norep.txt";
FILE *in;
FILE *out;
char word[30];
// (1) Read all words
in = fopen(infile, "r"); // .. and enforce success
while (fscanf(in, "%29s", word) == 1) {
// store word somewhere
}
fclose(in);
// (2) Determine unique words somehow
// (3) Write out unique words
out = fopen(outfile, "w"); // .. and enforce success
for (i = 0; i < nunique; i++) {
fprintf(out, "%s\n", unique[i]);
}
fclose(out);
return 0;
}
The actual algorithm to fin the unique words is missing from this incomplete skeleton code.
If you really want to test the words in a file for uniqueness without using additional memory beyond the current word, you can open the input file twice, with independent file pointers. Then you can write a loop like so:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main()
{
const char *infile = "text.txt";
const char *outfile = "norep.txt";
FILE *in1;
FILE *in2;
FILE *out;
char word1[30];
char word2[30];
in1 = fopen(infile, "r");
in2 = fopen(infile, "r");
out = fopen(outfile, "w");
if (in1 == NULL || in2 == NULL || out == NULL) {
fprintf(stderr, "Could not open all required files.\n");
exit(1);
}
while (fscanf(in1, "%29s", word1) == 1) {
int count = 0;
while (fscanf(in2, "%29s", word2) == 1) {
if (strcmp(word1, word2) == 0) count++;
if (count > 1) break;
}
if (count == 1) fprintf(out, "%s\n", word1);
rewind(in2);
}
fclose(in1);
fclose(in2);
fclose(out);
return 0;
}
This will, of course, re-read the file as often as there are words in the file. Not a good approach to find the unique words in Moby-Dick. I recommend that you look into the memory-based approach.
I have a file which contains the files names for every file in a directory. I am trying to open that file, read the file names from it and then open each file. However, I cannot get it to open the files. I have it printing the word it is reading and know it is reading correctly; however, it will not open the file. Any suggestions? My program is below.
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
FILE *in;
FILE *in2;
char inName[] = "inputfile.txt";
char *inName2;
inName2 = malloc(36 * sizeof (char));
char inPhrase[100];
if (( in = fopen(inName, "r")) == NULL )
{
printf("Can't open %s for reading.\n", inName);
return 2;
}
else
{
fgets(inName2, 36, in);
}
if (( in = fopen(inName2, "r")) == NULL )
{
printf("Can't open %s for reading. \n", inName2);
}
else
{
fgets(inPhrase, 100, in2);
printf("%s\n", inPhrase);
}
fclose(in);
fclose(in2);
return 0;
}
You have one outright typo and one mistake in your code. The line if (( in = fopen(inName2, "r")) == NULL ) should open in2 instead: if (( in2 = fopen(inName2, "r")) == NULL ). Your error message almost certainly reads something like this:
Can't open test_file.txt
for reading
Notice the newline that fgets always reads in for you. You should trim the line somehow. There are a few options available:
If your last line is guaranteed to be newline terminated, you can just remove the last character from each line: strchr(inName2, '\0')[-1] = '\0';.
You can trim the whitespace from the end of each line.
You can delete the last character only if it is \n (or possibly two characters, \r\n on Windows)
Final note: you should always post your error messages. If you were clever enough to interpret it properly in the first place, you would not be posting here, so don't expect us to take your word for where the program failed.
Do it this way
#include <stdio.h>
#include <stdlib.h>
int main() {
char inName[] = "inputfile.txt", * inName2;
FILE * in = fopen(inName, "r"), * in2;
char inPhrase[100];
size_t len;
// Check whether file opened correctly or display error
if (in == NULL) { perror(inName); return 1; }
// Read file line by line
while (getline(&inName2, &len, in) != -1) {
// Check if file opens otherwise go to next file
if ((in2 = fopen(inName2, "r")) == NULL) { perror(inName2); continue; }
// Read 100 chars from each file and display
fgets(inPhrase, 100, in2);
printf("%s\n", inPhrase);
fclose(in2);
}
fclose(in);
return 0;
}
I have to write a program witch reads from a file received by line and then it overwrites it with the read words uppercased.
This is my code
void toUpperCase(char* string) {
int i=0;
while(string[i])
{
string[i]=toupper(string[i]);
i++;
} }
int main(int argc, char** argv) {
if(argc==1)
{
puts("Error: INSERT PATH");
exit(0);
}
char* file=argv[1];
FILE* fd=fopen(file,"r+");
if(fd<0)
{
perror("Error opening file: ");
exit(0);
}
char buffer[30][30];
int i=0;
while(!feof(fd))
{
fscanf(fd,"%s",buffer[i]);
i++;
}
int j=0;
for(j=0; j<i; j++)
{
toUpperCase(buffer[j]);
fwrite(buffer[j],strlen(buffer[j]),1,fd);
}
fclose(fd);
return 0; }
but this program appends the words contained in buffer[][] instead of overwriting the file.
If the file contain was something like pippo pluto foo then, after the execution is pippo pluto fooPIPPOPLUTOFOO instead of PIPPO PLUTO FOO.
Where am i wrong? Thank you
You have to reset the file position indicator using fseek, as fscanf will advance it. Something like
fseek(fd, length_of_read_string, SEEK_CUR);
This allows you to read the file in chunks, but it will be tricky to get right. Or of course reset it to the file start because you read everything in 1 go:
fseek(fd, 0L, SEEK_SET);
I strongly recommend writing the modified data into a new file, and then after the program has run, delete the initial file and rename the new one. That will also take care of another issue with your program, you are reading the entire file into memory before handling it.
If you want to do in-place translation that doesn't change lengths, you can open the source file in two streams and then do read-chunk, write-chunk in lockstep. That has the advantage of being super-easy to convert to a non-in-place version that will work with nonseekable files too (stdin/stdout, pipes, and sockets).
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <ctype.h> //toupper
inline void upcaseStr(char* str){
for(;*str;str++) { *str=toupper(*str); }
}
int upcaseStream(FILE* in, FILE* out){
char buf[BUFSIZ]; //BUFSIZ is an implementation-defined constant for an optimal buffer size
while(fgets(buf, BUFSIZ, in)){
upcaseStr(buf);
if(fputs(buf, out) == EOF){ return 1; }
}
if(!feof){ return 1; }
return 0;
}
int main(int argc, char **argv)
{
//default in and out
FILE* in = stdin;
FILE* out = stdout;
if(argc == 2) {
in = fopen(argv[1], "r"); //for reading
out = fopen(argv[1], "r+"); //for writing (and reading) starting at the beginning
if(!(in && out)){
fprintf(stderr, "Error opening file %s for reading and writing: %s\n", argv[1], strerror(errno));
}
}
return upcaseStream(in, out);
}
If you do use the in-place version, then in the unlikely event that the if(fputs(buf, out) == EOF){ return 1; } line should return, you're screwed unless you have a backup copy of the file. :)
Note:
You shouldn't name your FILE pointers fd because C people will tend to think you mean "file descriptor". FILE is a struct around a file descriptor. A file descriptor is just an int that you can use for FILE access with the raw system calls. FILE streams are an abstraction layer on top of file descriptors--they aren't file descriptors.
As you read from the file, its internal position indicator gets moved. Once you start writing, you start writing from that position on, which happens to be at the end of the file. So you effectively append the data to the file.
Rewind the handle to reset the position indicator before writing into the file:
rewind(fp);
On a side note, you are reading the file incorrectly:
while(!feof(fd))
{
fscanf(fd,"%s",buffer[i]);
i++;
}
When you reach the end of the file, fscanf will return an error and not read anything, yet you still increment variable i, as if the read was successful. And then you check feof() for end-of-file, but i was already incremented.
Check feof() and return of fscanf() immediately after calling fscanf():
while(1)
{
int read = fscanf(fd,"%s",buffer[i]);
if( read != 1 )
//handle invalid read
if( feof(fd) )
break;
i++;
}
Think about what happens if the string is longer than 29 characters and/or the file contains more than 30 strings. char buffer[30][30];
Welcome to StackOverflow!
Reopening the stream with fopen with the "w" parameter:
fd=fopen(file, "w");
It opens the file and if there are any contents in the file, it clears them.