I would like to open a directory using opendir but am seeing something unexpected. opendir works for the string returned from getcwd but not the string from my helper function read_cwd, even though the strings appear to be equal.
If I print the strings, both print /Users/gwg/x, which is the current working directory.
Here is my code:
char real_cwd[255];
getcwd(real_cwd, sizeof(real_cwd));
/* This reads a virtual working directory from a file */
char virt_cwd[255];
read_cwd(virt_cwd);
/* This prints "1" */
printf("%d\n", strcmp(real_cwd, virt_cwd) != 0);
/* This works for real_cwd but not virt_cwd */
DIR *d = opendir(/* real_cwd | virt_cwd */);
Here is the code for read_cwd:
char *read_cwd(char *cwd_buff)
{
FILE *f = fopen(X_PATH_FILE, "r");
fgets(cwd_buff, 80, f);
printf("Read cwd %s\n", cwd_buff);
fclose(f);
return cwd_buff;
}
The function fgets includes the final newline in the buffer — so the second string is actually "/Users/gwg/x\n".
The simplest (but not necessarily the cleanest) way to solve this issue is to overwrite the newline with a '\0': add the following at the end of the function read_cwd:
n = strlen(cwd_buff);
if(n > 0 && cwd_buff[n - 1] == '\n')
cwd_buff[n - 1] = '\0';
fgets() includes the newline.
Parsing stops if end-of-file occurs or a newline character is found, in which case str will contain that newline character. — http://en.cppreference.com/w/c/io/fgets
You should trim the white space on both ends of the string when reading input like this.
From the fgets man page:
fgets() reads in at most one less than size characters from stream and
stores them into the buffer pointed to by s. Reading stops after an
EOF or a newline. If a newline is read, it is stored into the buffer.
A terminating null byte (aq\0aq) is stored after the last character in
the buffer.
You need to remove the newline character from the string you are reading in.
Related
I'm reading lines from a file and I might have a comment anywhere throughout it of any size.
while (fgets(line, 100, myFile))
{
// skip and print comment
if (line[0] == '#') printf("Comment is = %s", line);
else {...}
}
The code is doing what is supposed to until it gets a comment which is over 100 characters. In that case it will not detect the # anymore and it won't skip it. How can I solve this?
You could introduce a state variable to tell the program that you are on comment
mode. Like this:
// mode == 0 --> normal
// mode == 1 --> comment, remove/ignore comments
int mode = 0;
char line[100];
while(fgets(line, sizeof line, myFile))
{
char *newline = strchr(line, '\n');
if(mode == 1)
{
if(newline)
mode = 0; // restore normal mode
continue; // ignore read characters
}
char *comment = strchr(line, '#');
if(comment)
{
*comment = '\0';
if(newline == NULL)
mode = 1; // set comment mode
}
// process your line without the comment
}
If a comment is found, strchr returns a pointer to that location. Setting it
to '\0' allows you to process the line without a comment. If the comment is
larger than line can hold, then the newline character is not found. In that case
you have to skip the next read bytes of fgets, until you find a newline.
That's when the mode variable comes in handy, you set it to 1 so the next
iterations can ignore the line if a newline is not found.
fgets itself will tell you when it did not read the entire line:
The fgets() function reads bytes from the stream into the array pointed to by s, until n−1 bytes are read, or a newline character is read and transferred to s, or an end-of-file condition is encountered. The string is then terminated with a null byte.
(https://docs.oracle.com/cd/E36784_01/html/E36874/fgets-3c.html)
So if your just-read line starts with # but does not end with \n (and fgets does not indicate EOF), read and skip all next 'lines' until you either find the end of the current 'line' indicated by a terminating \n, or you encounter an EOF condition.
If you want to store the comment for later display (as you are doing), use malloc and realloc to create and enlarge memory for the comment itself. Do not forget to free it after you're done.
I'm trying to write a function that removes the rest of a line in C. I'm passing in a char array and a file pointer (which the char array was read from). The array is only supposed to have 80 chars in it, and if there isn't a newline in the array, read (and discard) characters in the file until you reach it (newline). Here's what I have so far, but it doesn't seem to be working, and I'm not sure what I'm doing wrong. Any help would be greatly appreciated! Here's the given information about what the function should do:
discardRest - if the fgets didn't read a newline than an entire line hasn't been read. This function takes as input the most recently read line and the pointer to the file being read. discardRest looks for the newline character in the input line. If newline character is not in the line, the function reads (and discards) characters from the file until the newline is read. This will cause the file pointer to be positioned to the beginning of the next line in the input file.
And here's the code:
void discardRest(char line[], FILE* file)
{
bool newlineFound = FALSE;
int i;
for(i = 0; i < sizeof(line); i++)
{
if(line[i] == '\n') newlineFound = TRUE;
}
if(!newlineFound)
{
int c = getc(file);
while(c != '\n')
{
c = getc(file);
}
}
}
Your way is much too difficult, besides sizeof always giving the size of its operand, which is a pointer and not the array it points to which you think it is.
fgets has thefollowing contract:
return NULL: Some kind of error, do not use the buffer, its content might be indeterminate.
otherwise the buffer contains a 0-terminated string, with the last non-0 being the retained '\n' if the buffer and the file were both large enough.
Thus, this should work:
So, use strlen() to get the buffer length.
Determine if a whole line was read (length && [length-1] == '\n').
As appropriate:
remove the newline character and return.
discard the rest of the line like you tried.
I am reading from a text file, iterating with a while(!feof) loop,
but whenever I use this condition the loop iterates an extra time.
I solved the problem with this 'patchy' code
while (stop == FALSE)
{
...
terminator = fgetc(input);
if (terminator == EOF)
stop = TRUE;
else
fseek(input, -1, SEEK_CUR);
}
But it looks and feels very bad.
You can take advantage of the fact that an assignment gets evaluated as the value being assigned, in this case to the character being read:
while((terminator = fgetc(input))!= EOF) {
// ...
}
Here is an idiomatic example (source):
fp = fopen("datafile.txt", "r"); // error check this!
// this while-statement assigns into c, and then checks against EOF:
while((c = fgetc(fp)) != EOF) {
/* ... */
}
fclose(fp);
Similarly you ca read line-by-line:
char buf[MAXLINE];
// ...
while((fgets(buf,MAXLINE,stdin)) != NULL) {
do_something(buf);
}
Since fgets copies the detected newline character, you can detect
end of line by checking the second to last buffer element. You can use
realloc to resize the buffer (be sure you keep a pointer to the beginning of the buffer, but pass buf+n, to the next fgets, where n is the number of read characters). From the standard regarding fgets:
Reads characters from stream and stores them as a C string into str until (num-1) characters have been read or either a newline or the end-of-file is reached, whichever happens first. A newline character makes fgets stop reading, but it is considered a valid character by the function and included in the string copied to str.
Alternatively, you could read the whole file in one go using fread() (see example following the link).
gcc 4.4.4 c89
I am using the following code to read in file using fgets. I just want to get the gender which could be either M or F.
However, as the gender is always the last character in the string. I thought I could get the character by using strlen. However, for some reason I have to get the strlen and minus 2. I know that the strlen doesn't include the nul. However, it will include the carriage return.
The exact line of text I am reading in is this:
"Low, Lisa" 35 F
My code:
int read_char(FILE *fp)
{
#define STRING_SIZE 30
char temp[STRING_SIZE] = {0};
int len = 0;
fgets(temp, STRING_SIZE, fp);
if(temp == NULL) {
fprintf(stderr, "Text file corrupted\n");
return FALSE;
}
len = strlen(temp);
return temp[len - 2];
}
The strlen returns 17 when I feel it should return 16. String length including the carriage return. I feel I should be doing - 1 instead of - 2.
Any suggestions if you understand my question.
Thanks,
EDIT:
fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops
after an EOF or a newline. If a newline is read, it is stored into the buffer. A '\0' is stored after the last character in the
buffer
So the buffer will contain:
"Low, Lisa" 35 F\0\r
Which will return 17 from strlen if it is including the \r? Am I correct in thinking that?
The buffer will contain:
"Low, Lisa" 35 F\n\0
so -2 is correct: strlen - 0 would be the null terminator, -1 the newline, and -2 is the letter F.
Also,
if(temp == NULL) {
temp is an array - it can never be NULL.
Instead of
if (temp == NULL)
check the return value from fgets instead, if its null then that would indicate failure
if ( fgets(temp, STRING_SIZE, fp) == NULL )
yes, strlen includes the newline
note that if you are on the last line of the file and there is no \n at the end of that line you make encounter a problem if you assume there is always \n in the string.
an alternative way would be to read the string as you do but check the last char, if there is no \n then you shouldn't use -2 offset but -1 instead.
It depends on the operating system used to save the files :
for Windows, carriage returns are \r\n
for Linux, they are \n
Did u debug and find what exactly coming at Len. If u are doing it in c add watch and find out the what is displaying at your value len.
Does fgets() always terminate the char buffer with \0 even if EOF is already reached? It looks like it does (it certainly does in the implementation presented in the ANSI K&R book), but I thought I would ask to be sure.
I guess this question applies to other similar functions such as gets().
EDIT: I know that \0 is appended during "normal" circumstances, my question is targeted at EOF or error conditions. For example:
FILE *fp;
char b[128];
/* ... */
if (feof(fp)) {
/* is \0 appended after EACH of these calls? */
fgets(b, 128, fp);
fgets(b, 128, fp);
fgets(b, 128, fp);
}
fgets does always add a '\0' to the read buffer, it reads at most size - 1 characters from the stream (size being the second parameter) because of this.
Never use gets as you can never guarantee that it won't overflow any buffer that you give it, so while it technically does always terminate the read string this doesn't actually help.
Never use gets!!
7.19.7.2 The fgets function
Synopsis
1 #include <stdio.h>
char *fgets(char * restrict s, int n,
FILE * restrict stream);
Description
2 The fgets function reads at most one less than the number of characters
specified by n from the stream pointed to by stream into the array pointed
to by s. No additional characters are read after a new-line character
(which is retained) or after end-of-file. A null character is written
immediately after the last character read into the array.
Returns
3 The fgets function returns s if successful. If end-of-file is encountered
and no characters have been read into the array, the contents of the array
remain unchanged and a null pointer is returned. If a read error occurs
during the operation, the array contents are indeterminate and a null
pointer is returned.
So, yes, when fgets() does not return NULL the destination array always has a null character.
If fgets() returns NULL, the destination array may have been changed and may not have a null character. Never rely on the array after getting NULL from fgets().
Edit example added
$ cat fgets_error.c
#include <stdio.h>
void print_buf(char *buf, size_t len) {
int k;
printf("%02X", buf[0]);
for (k=1; k<len; k++) printf(" %02X", buf[k]);
}
int main(void) {
char buf[3] = {1, 1, 1};
char *r;
printf("Enter CTRL+D: ");
fflush(stdout);
r = fgets(buf, sizeof buf, stdin);
printf("\nfgets returned %p, buf has [", (void*)r);
print_buf(buf, sizeof buf);
printf("]\n");
return 0;
}
$ ./a.out
Enter CTRL+D:
fgets returned (nil), buf has [01 01 01]
$
See? no NUL in buf :)
man fgets:
fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a new‐line is read, it is stored into the buffer. A '\0' is stored after the last character in the buffer.
If you did open the file in binary mode "rb", and if you want to read Text line by line by using fgets you can use the following code to protect your software of loosing text, if by a mistake the text contained a '\0' byte.
But finally like the others mentioned, normally you should not use fgets if the stream contains '\0'.
size_t filepos=ftell(stream);
fgets(buffer, buffersize, stream);
len=strlen(buffer);
/* now check for > len+1 since no problem if the
last byte is 0 */
if(ftell(stream)-filepos > len+1)
{
if(!len) filepos++;
if(!fseek(stream, filepos, SEEK_SET) && len)
{
fread(buffer, 1, len, stream);
buffer[len]='\0';
}
}
Yes it does. From CPlusPlus.com
Reads characters from stream and stores them as a C string into str until (num-1) characters have been read or either a newline or a the End-of-File is reached, whichever comes first.
A newline character makes fgets stop reading, but it is considered a valid character and therefore it is included in the string copied to str.
A null character is automatically appended in str after the characters read to signal the end of the C string.