This function is passed the path of a text file(mapper_path) which contains paths to other text files on each line. I am supposed to open the mapper_path.txt file, then open and evaluate each of the paths within it (example in output).
fopen succeeds on the mapper_path file but fails on the paths which it contains.
In the failure condition, it prints the EXACT path I'm trying to open.
I'm working in C on windows and running commands on Ubuntu subsystem.
How can I properly read and store the sub-path into a variable to open it?
SOLVED with Rici's suggestion!
int processText(char * mapper_path, tuple * letters[])
{
char line[LINE_SIZE];
char txt_path[MAX_PATH];
FILE * mapper_fp = fopen(mapper_path, "r");
if(!mapper_fp)
{
printf("Failed to open mapper path: %s \n", mapper_path);
return -1;
}
//!!! PROBLEM IS HERE !!!
while(fgets(txt_path, MAX_PATH, mapper_fp))
{
//remove newline character from end
txt_path[strlen(txt_path)-1] = 0;
//open each txt file path, return -1 if it fails
FILE* fp = fopen(txt_path, "r");
if(!fp)
{
printf("Failed to open file path:%s\n", txt_path);
return -1;
}
//...more unimportant code
prints:
Failed to open filepath:
/mnt/c/users/adam/documents/csci_4061/projects/blackbeards/testtext.txt
This is the exact path of the file i am trying to open.
I suspect that the problem is related to this:
I'm working in C on windows and running commands on Ubuntu subsystem.
Presumably, you created the mapper.txt file using Windows tools, so it has Windows line endings. However, I think the Ubuntu subsystem does not know about Windows line endings, and so even though you open the file in mode 'r', it does not translate CR-LF into a single \n. When you then remove the \n at the end of the input, you still leave the \r.
That \r won't be visible when you print out the line, since all it does is move the cursor to the beginning of the line and the next character output is a \n. It's usually a good idea to surround strings with other text when you print debugging messages, since that can give you a clue about this sort of problem. If you'd used:
printf("Failed to open file path: '%s'\n", txt_path);
you might have seen the error:
'ailed to open filepath: '/mnt/c/users/adam/documents/csci_4061/projects/blackbeards/testtext.txt
Here, the hint that there is a \r at the end of the string is the overwriting of the first character of the message with the trailing apostrophe.
It's not quite accurate to say that fgets "adds a \n character to the end [of the line read]." It's more accurate to say that it doesn't remove that character, if it is present. It is quite possible that there isn't a newline at the end of the line. The line may be the last line in a text file which doesn't end with a newline character, for example. Or the fgets might have been terminated by reaching the character limit you supplied, rather than by finding a newline character.
So you are certainly better off using the getline interface, which has two advantages: (a) it allocates storage for the line itself, so you don't need to guess a maximum length in advance, and (b) it tells you exactly how many characters it read, so you don't have to count them.
Using that information, you can then remove a \n which happens to be at the end of the line, if there is one, and then remove the preceding \r, if there is one:
char* line = NULL;
size_t n_line = 0;
for (;;) {
ssize_t n_read = getline(&line, &n_line, mapper_fp);
if (n_read < 0) break; /* EOF or some kind of read error */
if (n_read > 0 && line[n_read - 1] == '\n')
line[nread--] = 0;
if (n_read > 0 && line[n_read - 1] == '\r')
line[nread--] = 0;
if (nread == 0) continue; /* blank line */
/* Handle the line read */
}
if (ferr(mapper_fp))
perror("Error reading mapper file");
free(line);
Related
Today I decided to learn to code for the first time in my life. I decided to learn C. I have created a small program that checks a txt file for a specific value. If it finds that value then it will tell you that that specific value has been found.
What I would like to do is that I can put multiple files go through this program. I want this program to be able to scan all files in a folder for a specific string and display what files contain that string (basically a file index)
I just started today and I'm 15 years old so I don't know if my assumptions are correct on how this can be done and I'm sorry if it may sound stupid but I have been thinking of maybe creating a thread for every directory I put into this program and each thread individually runs that code on the single file and then it displays all the directories in which the string can be found.
I have been looking into threading but I don't quite understand it. Here's the working code for one file at a time. Does anyone know how to make this work as I want it?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
//searches for this string in a txt file
char searchforthis[200];
//file name to display at output
char ch, file_name[200];
FILE *fp;
//Asks for full directory of txt file (example: C:\users\...) and reads that file.
//fp is content of file
printf("Enter name of a file you wish to check:\n");
gets(file_name);
fp = fopen(file_name, "r"); // read mode
//If there's no data inside the file it displays following error message
if (fp == NULL)
{
perror("Error while opening the file.\n");
exit(EXIT_FAILURE);
}
//asks for string (what has to be searched)
printf("Enter what you want to search: \n");
scanf("%s", searchforthis);
char* p;
// Find first occurrence of searchforthis in fp
p = strstr(searchforthis, fp);
// Prints the result
if (p) {
printf("This Value was found in following file:\n%s", file_name);
} else
printf("This Value has not been found.\n");
fclose(fp);
return 0;
}
This line,
p = strstr(searchforthis, fp);
is wrong. strstr() is defined as, char *strstr(const char *haystack, const char *needle), no file pointers in it.
Forget about gets(), its prone to overflow, reference, Why is the gets function so dangerous that it should not be used?.
Your scanf("%s",...) is equally dangerous to using gets() as you don't limit the character to be read. Instead, you could re-format it as,
scanf("%199s", searchforthis); /* 199 characters + \0 to mark the end of the string */
Also check the return value of scanf() , in case an input error occurs, final code should look like this,
if (scanf("%199s", searchforthis) != 1)
{
exit(EXIT_FAILURE);
}
It is even better, if you use fgets() for this, though keep in mind that fgets() will also save the newline character in the buffer, you are going to have to strip it manually.
To actually perform checks on the file, you have to read the file line by line, by using a function like, fgets() or fscanf(), or POSIX getline() and then use strstr() on each line to determine if you have a match or not, something like this should work,
char *p;
char buff[500];
int flag = 0, lines = 1;
while (fgets(buff, sizeof(buff), fp) != NULL)
{
size_t len = strlen(buff); /* get the length of the string */
if (len > 0 && buff[len - 1] == '\n') /* check if the last character is the newline character */
{
buff[len - 1] = '\0'; /* place \0 in the place of \n */
}
p = strstr(buff, searchforthis);
if (p != NULL)
{
/* match - set flag to 1 */
flag = 1;
break;
}
}
if (flag == 0)
{
printf("This Value has not been found.\n");
}
else
{
printf("This Value was found in following file:\n%s", file_name);
}
flag is used to determine whether or not searchforthis exists in the file.
Side note, if the line contains more than 499 characters, you will need a larger buffer, or a different function, consider getline() for that case, or even a custom one reading character by character.
If you want to do this for multiple files, you have to place the whole process in a loop. For example,
for (int i = 0; i < 5; i++) /* this will execute 5 times */
{
printf("Enter name of a file you wish to check:\n");
...
}
This is the code.
FILE* fPtr;
FILE* fTemp;
char path[100];
char buffer[BUFFER_SIZE];
char newline[BUFFER_SIZE];
int line, count;
printf("Enter path of source file: ");
scanf("%s", path);
printf("Enter line number to replace: ");
scanf("%d", &line);
/* Remove extra new line character from stdin */
fflush(stdin);
printf("Replace '%d' line with: ", line);
scanf("%s", &newline);
/* Open all required files */
fPtr = fopen(path, "r");
fTemp = fopen("replace.tmp", "w");
/* fopen() return NULL if unable to open file in given mode. */
if (!fPtr)
{
/* Unable to open file hence exit */
printf("\nUnable to open file.\n");
printf("Please check whether file exists and you have read/write privilege.\n");
exit(EXIT_SUCCESS);
}
/*
* Read line from source file and write to destination
* file after replacing given line.
*/
count = 0;
while ((fgets(buffer, BUFFER_SIZE, fPtr)) != 0)
{
count++;
/* If current line is line to replace */
if (count == line)
fputs(newline, fTemp);
else
fputs(buffer, fTemp);
}
/* Close all files to release resource */
fclose(fPtr);
fclose(fTemp);
/* Delete original source file */
remove(path);
/* Rename temporary file as original file */
rename("replace.tmp", path);
printf("\nSuccessfully replaced '%d' line with '%s'.", line, newline);
return 0;
I wanted to replace a line supposedly the content of the text file is this
> Andy,06/05/2000,US,0654852,254845,313132
> Fan,865644,4654654,654654,465456
> Ben,04/01/1995,SG,0674874,213454,132158
Supposedly I wanted to change the of Fan so I run the code above, it gave me this. I do not want this to happen.
> Andy,06/05/2000,US,0654852,254845,313132
> Fanny,865644,4654654,654654,465456Ben,04/01/1995,SG,0674874,213454,132158
And if I want to change the name of Andy it gave me this
Landy,06/05/2000,US,0654852,254845,313132Fanny,865644,4654654,654654,465456Ben,04/01/1995,SG,0674874,213454,13215
Why it does that?
How do I delete specific line and replace it ?
Assume that the replacement line has a different size than the original one. You cannot do that in standard C11 (check n1570) without copying the file to a new place (because you cannot overwrite a sequence of bytes in the middle of a file by another sequence of different length).
Read carefully the documentation of <stdio.h>
Lines are just a convention in C: they are ending by some end-of-line character (\n). A file could have a single line and contain a megabyte.
So you could use getline to read lines. Or use fgets. In both cases you should check for failure. With fgets what would happen if the line is bigger than the buffer? With getline what would happen with a file containing a single line of a gigabyte which does not fit into memory?
Be aware that stdout is buffered (and the buffer size could vary from one run to the next one and could be different if you use command pipelines). See setvbuf and fflush. In practice, take the habit of ending your printf format control string with \n and/or explicitly calling fflush
Many open source programs doing what you want already exist. GNU ed comes to mind. Consider studying its source code for inspiration.
Please read how to debug small programs. If you use a recent GCC compiler with some GDB debugger, compile with all warnings and debug info, so gcc -Wall -Wextra -g then use gdb to understand the behavior of your program. Specify on paper the input file syntax using EBNF and read more about parsing techniques, including recursive descent parsing.
Notice that:
fflush(stdin);
is undefined behavior. You should fflush output streams only.
PS. You could later read about databases then consider using sqlite.
fgets will read from the file up to and including the newline character at the end of the line. The scanf call you use to get the replacement string does not, so when you write out newline it does not contain a newline character.
Solutions include explicitly adding the newline (possibly with fputc('\n', fTemp);, or using fgets(newline, BUFFER_SIZE, stdin); instead of the scanf to read your input string.
Yes I want to use
fgets(new,line,buffer_sizze,stdin);
but it just won't ask for input unless I put it inside of main().
When I put it inside of a function that I created, it won't ask for input from the user which is why I used scanf.
Is there a way to put it \n without asking the user to type \n.
Or any solution to why it's not getting input when I used fgets.
For the people that has the same problem as me.
Fgets not asking for any input.
Try use getchar().
That solved my problem.
For unknown reason.
why does read() on a file in linux add a newline character at EOF even if the file really does not have a newline character ?
my file data is :
1hello2hello3hello4hello5hello6hello7hello8hello9hello10hello11hello12hello13hello14hello15hello
my read() call on this file should hit EOF after reading the last 'o' in "15hello". I use the below :
while( (n = read(fd2, src, read_size-1)) != 0) // read_size = 21
{
//... some code
printf("%s",src);
//... some code
}
where fd2 is the file's descriptor. At the last loop, n was 17 and i had src[16] = '\n'. So......, does the read call in linux add a newline at EOF?
does the read call in linux add a newline at EOF?
No.
Your input file likely has a terminating newline in it - most well-formatted text files do, so multiple files can be concatenated without lines running together.
You could also be running into a stray newline character that was already in your buffer, because read() does not terminate the data read with a NUL character to create an actual C-style string. And I'd guess your code doesn't either, else you would have posted it. Which means your
printf("%s",src);
is quite likely undefined behavior.
why does read() on a file in linux add a newline character at EOF even if the file really does not have a newline character ? No, read() system call doesn't add any new line at end of file.
You are experiencing this kind of behavior because may be you have created text file using vi command and note that default new line gets added if you have created file using vi.
You can validate this on your system by creating a empty text file using vi and then run wc command on that.
Also you can read file data using read() system call all at once if you know the file size(find size using stat() system call) and can avoid while loop.
This
while( (n = read(fd2, src, read_size-1)) != 0) {
/* some code */
}
Change to
struct stat var;
stat(filename, &var); /* check the retuen value of stat()..having all file info now */
off_t size = var.st_size;
Now you have size of file, create one dynamic or stack array equal to size and read the data from file.
char *ptr = malloc(size + 1);
Now read all data at once like
read(fd,ptr,size);/*now ptr having all file contents */
And at last once work done, Don't forgot to free the ptr by calling free(ptr).
I have a file that's storing strings from the user's input (stdin)
However there are 2 situations
If I read it normally, my file will have an empty line at its end due to the newline from the last string the user introduced.
If I remove the \n from the input string, the file stores all strings in the same line, which is not wanted.
How can I simply remove that newline from the end of the file?
I can edit and provide some of my code if required.
EDIT: Let's say the last line of a file I already have is "cards"
when the cursor is in front of "cards", if I press the down arrow it doesn't go on to the next line, while in this case it can happen once.
For my code to function perfectly I can't let that happen,
Here's an example of what I have:
f=fopen(somefile, "w");
do
{
fgets(hobby, 50, stdin);
fprintf(f, "%s", hobby)
} while(strcmp(hobby,"\n") != 0);
The newline character1 at the end of the file is part of the last line. Removing it is possible but makes the last line incomplete, which will break many programs. For example, concatenating another file at the end of this file will cause the last line to be merged with the first line of the concatenated file.
A Yuri Laguardia commented, if you later reopen this file in append mode to write more lines, the first one added would be concatenated at the end of this last incomplete line. Probably not the intended behavior.
If you do not want the file to contain empty lines, check user input before writing the line into the file:
void input_file_contents(FILE *fp) {
char userinput[80];
printf("enter file contents:\n");
while (fgets(userinput, sizeof userinput, stdin)) {
if (*userinput != '\n') {
fputs(userinput, fp);
}
}
}
EDIT:
Your code does not test for termination at the right place: you write the empty line before the test. Do not use a do / while loop:
f = fopen(somefile, "w");
if (f != NULL) {
/* read lines until end of file or empty line */
while (fgets(hobby, 50, stdin) != NULL && *hobby != '\n') {
fputs(hobby, f);
}
}
1 The newline character is actually a pair of bytes <CR><LF> on legacy systems.
A friend of mine needs to use MATLAB for one of his classes, so he called me up (a Computer Science Major) and asked if I could teach him C. I am familiar with C++, so I am also familiar with the general syntax, but had to read up on the IO library for C.
I was creating some simple IO programs to show my friend, but my third program is causing me trouble. When I run the program on my machine using Eclipse (with the CDT) Eclipse's console produces a glitchy output where instead of prompting me for the data, it gets the input and then prints it all at once with FAILURE.
The program is supposed to get a filename from user, create the file, and write to it until the user enters a blank line.
When I compile/run it on my machine via console (g++ files2.c) I am prompted for the data properly, but FAILURE shows up, and there is no output file.
I think the error lies with how I am using the char arrays, since using scanf to get the filename will create a functional file (probably since it ignores whitespace), but not enter the while loop.
#include <stdio.h>
#define name_length 20
#define line_size 80
int main() {
FILE * write_file; // pointer to file you will write to
char filename[name_length]; // variable to hold the name of file
char string_buffer[line_size]; // buffer to hold your text
printf("Filename: "); // prompt for filename
fgets(filename, name_length, stdin); // get filename from user
if (filename[name_length-1] == '\n') // if last char in stream is newline,
{filename[name_length-1] = '\0';} // remove it
write_file = fopen(filename, "w"); // create/overwrite file user named
if (!write_file) {printf("FAILURE");} // failed to create FILE *
// inform user how to exit
printf("To exit, enter a blank line (no spaces)\n");
// while getting input, print to file
while (fgets(string_buffer, line_size, stdin) != NULL) {
fputs(string_buffer, write_file);
if (string_buffer[0] == '\n') {break;}
}
fclose(write_file);
return 0;
}
How should I go about fixing the program? I have found next to nothing on user-terminated input being written to file.
Now if you will excuse me, I have a couple of files to delete off of my University's UNIX server, and I cannot specify them by name since they were created with convoluted filenames...
EDIT------
Like I said, I was able to use
scanf("%s", filename);
to get a working filename (without the newline char). But regardless of if I use scanf or fgets for my while loop, if I use them in conjunction with scanf for the filename, I am not able to write anything to file, as it does not enter the while loop.
How should I restructure my writing to file and my while loop?
Your check for the newline is wrong; you're looking at the last character in filename but it may be before that if the user enters a filename that's shorter than the maximum. You're then trying to open a file that has a newline in it's name.
These lines seem to be incorrect:
if (filename[name_length-1] == '\n') // if last char in stream is newline,
{filename[name_length-1] = '\0';} // remove it
You verify the name_length - 1 character,, which is 19 in your case without any regard of the introduced filename's length. So if your file name's length is less then 18 you won't replace the '\n' character at the end of your string. Obviously the file name can't contain '\n' character.
You need to get the size of you file name first with strlen() as an example.
if (filename[strlen(filename) - 1] == '\n')
{
filename[strlen(filename) - 1] = '\0';
}
(Don't forget to include the string.h header)
I hope I was able to help with my weak english.