I want to fscanf a csv file which is a output of fprintf, I set the same format but it didn't work, which means when I use that function to fscanf the file I just made, it didn't succuss, even didn't get into the while-loop. So, how to modify it to make it work?
Below part of my code
part of fprintf
fp = fopen("Out.csv", "w");
fprintf(fp, "%99s,%d,%99s,%99s\n", current->group, current->id, current->name,
current->address);
part of fscanf
fp = fopen("Out.csv", "r");
while (fscanf(fp, "%99s,%d,%99s,%99s\n", group, &id, name, address) == 4) {
head = push_sort(head, group, name, id, address);
printf("%99s", name);
}
I suspect it's because "%s" specifier in *scanf() family stops scanning when it finds a white space, you can tell fscanf() which specific character to ignore, and it will stop at that character.
I believe the following format string will work
"%99[^,],%d,%99[^,],%99[^,\n]\n"
read this link to find out why I think the pattern will work, search specifically for the [ specifier.
The *scanf() functions are hard, it's always difficult to make them work correctly, although if you are generating the line and you're sure of what it contains and no surprises will happen, you can trust it to work.
You will be safe if you check the return value, which you do, so if you fail to read lines that you consider valid, then you can try to fgets() a line from the file, and parse it with strchr() or strtok(), I prefer strchr() because
It doesn't need to alter the input string.
It's thread safe and reentrant.
It allows you to infere more, like the length of the token, without strlen().
Related
Very new to programming. I am trying to create a txt file that asks the user for a file name and then text for the file. At first I got an error about a null so I put an if statement and it seems like the code cannot open the file with that name. Tried to do some research and ended up making some changes but still resulted in the same error.
include <stdio.h>;
include <stdlib.h>;
int main()
{
char *fileName[100];
char inputText[100];
printf("What is the .txt file name? \n");
scanf_s("%123s", &fileName);
strcat(fileName, ".txt");
FILE *textFile;
textFile = fopen_s(&textFile, fileName, "w");
if (textFile != 0)
{
printf("Cannot get file");
return -1;
}
printf("What should be written in the text file? \n");
scanf_s("%123s", &inputText);
fprintf(textFile, "%s", inputText);
fclose(textFile);
return 0;
}
The most important thing you can do in learning C is to -- slow down. There is a lot to learn and you have to take it one step at a time. There is no use in guessing, compiling, seeing if anything changes, changing something else and (repeat). Look it up.
That said, you are interested in basic input/output to/from stdin/stdout and to a file opened for writing. It is unclear whether you are working on windows with scanf_s or using the non _s version as they are mixed and matched below. Regardless, the primary difference there will be the required parameters.
In C, you declare the arrays to hold your filename and inputtext to hold 100 characters. When working with strings, each string requires a nul-terminating character at the end ('\0'... or just 0, numerically the same). That means you can store a maximum of 99 characters +1 nul-terminating character in either filename or inputtext (side note: C generally avoids mixed-case variable names in favor of all lower-case, but that is up to you)
To protect against writing beyond the end of your filename or inputtext, you need to insure that you limit the number of characters you attempt to store in either. You do that with the field-width option to the format specifier. e.g.,
scanf ("%99s", inputtext);
or for the windows _s version:
scanf_s ("%99s", inputtext, 100u);
However, using a format specifier of "%99s" does not allow the input to include whitespace as the %s format specifier will read up to the first whitespace or newline. Second, it does NOT read (or in anyway handle) the '\n' at the end of user-input generated as the result of pressing [Enter]. This will cause problems if your next input is character input as scanf will happily accept '\n' as the next character to be read. Now %s will skip leading whitespace ('\n' being whitespace) should not present a problem, but this is the level of thought process you must go through in forming something as simple as your scanf format string.
Get in the habit of accounting for all characters in the input stream every time. That way you are not caught off-guard with some error you cannot explain.
To allow your input to contain whitespace, you can use a character class format specifier for scanf. For instance you could use "%99[^\n]" as the format string. However the character class does not automatically ignore leading whitespace, but you can provide that flexibility by leaving a space before the % beginning the format specifier, e.g. " %99[^\n]". It is important. (it is also why fgets or POSIX getline are generally preferred over scanf for handling user-input.
Now how do you handle the '\n' you left in the input buffer (e.g. stdin here)? In addition to leaving the space, you can make use of the assignment suppression operator within the format string. " %99[^\n]%*c" The %*c is a format specifier for reading a character %c, but by including the '*' (assignment suppression operator), you tell scanf to read and discard the character.
It is not enough to simply provide the correct format specifier when taking user-input. You must VALIDATE that you have actually received the input you expect. With any of the input routines, that, at minimum, means checking the return for scanf (or fgets or getline, etc..). For scanf, the return is the "match count", which is the number of successful conversions performed according the the format string. e.g. the %s (or %[^\n]) constitute a request for a single conversion. (any conversion associated with the assignment suppression operator is NOT included in the match count) So your anticipated return is the number of conversions in your format string. Putting that together, you could handle your inputtext with:
printf ("What should be written in the text file? "); /* prompt */
/* validate user input -- limit to 99 chars (+1 for nul char) */
if (scanf (" %99[^\n]%*c", inputtext) != 1) {
fprintf (stderr, "error: invalid input (inputtext).\n");
return 1;
}
Wouldn't the same thing also work for filename? Answer: No. Why? You plan on appending ".txt" to filename after entered by the user, right? How many characters are in ".txt"? Answer: 4 (you will only have 1 nul-terminating char for the combined string). So what must you limit filename to? " %95[^\n]%*c"
To do file I/O, you have several choices. By far the fstream buffered I/O functions are the most common for basic text I/O. In order to read from, or write to, a file, you must first open a FILE stream. You do that by declaring a FILE *pointer and then calling fopen and then checking the return (the value of pointer) to validate your file was successfully opened. The same rules, format specifiers, etc.. apply to reading/writing to a file (on disk), just as they do to writing to stdin or stdout as all are simply files from C's perspective.
With that in mind, you could do something similar to the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { MAXC = 100 };
int main (void) {
/* declare and initialize variables */
char filename[MAXC] = "", inputtext[MAXC] = "";
FILE *fp = NULL;
printf ("What is the .txt file name? "); /* prompt */
/* validate user input -- limit to 95 chars */
if (scanf (" %95[^\n]%*c", filename) != 1) {
fprintf (stderr, "error: invalid input (filename).\n");
return 1;
}
strcat (filename, ".txt"); /* +4 chars = 99 chars */
/* open file/validate file open for reading */
if (!(fp = fopen (filename, "w"))) {
fprintf (stderr, "error: file open failed '%s'.\n", filename);
return 1;
}
printf ("What should be written in the text file? "); /* prompt */
/* validate user input -- limit to 99 chars (+1 for nul char) */
if (scanf (" %99[^\n]%*c", inputtext) != 1) {
fprintf (stderr, "error: invalid input (inputtext).\n");
return 1;
}
/* output status to stdout & inputtext to fp */
printf ("\nwriting to '%s'\n%s\n", filename, inputtext);
fprintf (fp, "%s\n", inputtext);
if (fclose (fp)) /* close file - validate stream close */
fprintf (stderr, "error: on file stream close.\n");
return 0;
}
note: after writing to a file, it is important to check the return of fclose to insure a stream error did not occur during the write. (for closing streams you read from, that concern isn't there)
Example Use/Output
$ ./bin/inputtext
What is the .txt file name? dat/inputtext
What should be written in the text file? A quick brown fox jumps over the lazy dog.
writing to 'dat/inputtext.txt'
A quick brown fox jumps over the lazy dog.
Check the file contents:
$ cat dat/inputtext.txt
A quick brown fox jumps over the lazy dog.
Look the code over and let me know if you have any questions regarding any character in the code. Everyone needs a little help getting started, and the most important thing I can convey is to slow down and understand every character you code, read and understand your compiler warnings (fix every one), and if you are not sure about what you are doing, look it up. Either man function on Linux/Unix, or search MSDN for windows (e.g. scanf_s,...). They tell you in reasonably clear term what type and requirements there are for every parameter to every function (and a lot provide examples).
Good luck with your coding.
According to the MSDN documentation fopen_s takes an argument of form FILE** rather than FILE*. It also returns an error code rather than the file handle, which is not the return value of the function. So what you've done in this code is overwritten your file handle with some irrelevant integer. If you store your error code in a different variable it should resolve that issue.
More information on fopen_s can be found here: https://msdn.microsoft.com/en-us/library/z5hh6ee9.aspx
I am working on a c project and I need to remove a file from within a directory. For some reason though it keeps on saying that it can't delete because the file or directory doesn't exist. Below is the code that I am using to remove the file.
void deleteOldestLog()
{
FILE *fp;
char path[FILE_PATH_BUF_LEN], *fileName;
fp = popen("ls -tr /home/myfolder/logs/ |head -1", "r");
if (fp == NULL)
{
printf("Failed to run command");
}
else
{
char removalPath[FILE_PATH_BUF_LEN];
while ((fileName = fgets(path, sizeof(path)-1, fp)) != NULL)
{
sprintf(removalPath, "/home/myfolder/logs/%s", fileName, sizeof(fileName)-1);
printf("Removing file: %s", removalPath);
if (remove(removalPath) != 0)
{
perror("ERROR DELETING LOG");
}
else
{
printf("Successfully deleted %s", removalPath);
}
break;
}
pclose(fp);
}
}
Even though it says that it can't find the file because it doesn't exist I know that this isn't true because if I run ll followed by the path that the c program printed it returns the file that I am trying to delete.
I think it might be because fgets is putting '\0' on the end of the string which is stopping the remove from working.
How can I fix this?
There's a newline at the end of the file name read by fgets(). Your file name doesn't actually end with a newline.
You attempt to remove the newline with:
sprintf(removalPath, "/home/myfolder/%s", fileName, sizeof(fileName)-1);
However, to be effective, you'd need to use strlen() instead of sizeof(), and you'd need to modify the format string:
sprintf(removalPath, "/home/myfolder/%.*s", (int)strlen(fileName)-1, fileName);
The argument for the * must be an int and strlen() returns a size_t; hence the cast. (GCC will warn about that sort of thing if you turn on the warnings; use at least -Wall.)
A tip for you: when in doubt, print the string. I'll typically use a format like this. Note the angle brackets around the string:
printf("Removing: <<%s>>\n", removalPath);
When you see:
Removing: <</home/myfolder/something
>>
you know there's a problem with a newline in the string. Without the markers, you might not notice that there's a newline in the string causing the extra newline in the output.
Why does the format string need to be modified?
Let's look at the original sprintf() again:
sprintf(removalPath, "/home/myfolder/%s", fileName, sizeof(fileName)-1);
The format string expects 1 argument, a string. The call provides two values, a string and a length. So, the first problem is that there is a left-over argument. This usually does no damage, but be aware of it. Presumably, the reason for passing the length minus one was to lose the last character. The formats in the printf() family can be adorned with one or two numbers, and either or both can have a * instead of an integer value. These numbers constrain the lengths of the formatted value. When you write:
%.*s
you state the length of the output shall be exactly the length specified by an int value passed as an argument before the string itself. Hence the revision:
sprintf(removalPath, "/home/myfolder/%.*s", (int)strlen(fileName)-1, fileName);
(which I just fixed while adding this information.)
I've also not added error checking to the output of sprintf() etc. That's not unusual; however, best coding practices do ensure that functions like sprintf() return the value you expect (which is the number of characters written to the string, excluding the trailing null '\0'.
(Aside: in general, it is better to use snprintf() than sprintf(); that can avoid buffer overflows.
snprintf(removalPath, sizeof(removalPath), "/home/myfolder/%.*s",
(int)strlen(fileName)-1, fileName);
However, the behaviour of the *snprintf() functions under MSVC is different from the behaviour mandated by the C Standards (C99, C11). Worse, in the case of vsnprintf_s() and the other _s functions, the argument lists are different between the MSVC and the C Standard.)
I have been told that scanf should not be used when user inputs a string. Instead, go for gets() by most of the experts and also the users on StackOverflow. I never asked it on StackOverflow why one should not use scanf over gets for strings. This is not the actual question but answer to this question is greatly appreciated.
Now coming to the actual question. I came across this type of code -
scanf("%[^\n]s",a);
This reads a string until user inputs a new line character, considering the white spaces also as string.
Is there any problem if I use
scanf("%[^\n]s",a);
instead of gets?
Is gets more optimized than scanf function as it sounds, gets is purely dedicated to handle strings. Please let me know about this.
Update
This link helped me to understand it better.
gets(3) is dangerous and should be avoided at all costs. I cannot envision a use where gets(3) is not a security flaw.
scanf(3)'s %s is also dangerous -- you must use the "field width" specifier to indicate the size of the buffer you have allocated. Without the field width, this routine is as dangerous as gets(3):
char name[64];
scanf("%63s", name);
The GNU C library provides the a modifier to %s that allocates the buffer for you. This non-portable extension is probably less difficult to use correctly:
The GNU C library supports a nonstandard extension that
causes the library to dynamically allocate a string of
sufficient size for input strings for the %s and %a[range]
conversion specifiers. To make use of this feature, specify
a as a length modifier (thus %as or %a[range]). The caller
must free(3) the returned string, as in the following
example:
char *p;
int n;
errno = 0;
n = scanf("%a[a-z]", &p);
if (n == 1) {
printf("read: %s\n", p);
free(p);
} else if (errno != 0) {
perror("scanf");
} else {
fprintf(stderr, "No matching characters\n"):
}
As shown in the above example, it is only necessary to call
free(3) if the scanf() call successfully read a string.
Firstly, it is not clear what that s is doing in your format string. The %[^\n] part is a self-sufficient format specifier. It is not a modifier for %s format, as you seem to believe. This means that "%[^\n]s" format string will be interpreted by scanf as two independent format specifiers: %[^\n] followed by a lone s. This will direct scanf to read everything until \n is encountered (leaving \n unread), and then require that the next input character is s. This just doesn't make any sense. No input will match such self-contradictory format.
Secondly, what was apparently meant is scanf("%[^\n]", a). This is somewhat close to [no longer available] gets (or fgets), but it is not the same. scanf requires that each format specifiers matches at least one input character. scanf will fail and abort if it cannot match any input characters for the requested format specifier. This means that scanf("%[^\n]",a) is not capable of reading empty input lines, i.e. lines that contain \n character immediately. If you feed such a line into the above scanf, it will return 0 to indicate failure and leave a unchanged. That's very different from how typical line-based input functions work.
(This is a rather surprising and seemingly illogical properly of %[] format. Personally, I'd prefer %[] to be able to match empty sequences and produce empty strings, but that's not how standard scanf works.)
If you want to read the input in line-by-lane fashion, fgets is your best option.
Say If i want an input to be
[Name] [Name]
How would I detect
[Name] [Name] [Name]
and return error?
Here is what I have so far,
char in[20];
char out[20];
scanf(" %s %s", out, in);
scanf returns the number of validly converted arguments. So in your first case, the return value would be 2, in the latter case 3.
To check the right amount of parameters, this might help:
char in[20];
char out[20];
char error[20];
int check;
check = scanf(" %s %s %s", out, in, error);
if(check != 2)
{
// TODO: error handling
}
EDIT: now it should be working, see comments below.
Of course, as stated by other posters: scanf is not considered a quite safe function since buffer overflows can occur, and you should avoid using it. It is better to read the inputs to a buffer with fgets() and the try to parse the arguments you want.
This is homework, so you might be required to work under certain (arbitrary) restrictions. However, the phrase "scanf error handling" is something of an oxymoron in the C programming language.
The best way to do this is to read in a line/other suitable chunk and parse it with C string functions. You can do it in one line of scanf but there are many drawbacks:
You can guard against buffer overflows, but if a buffer isn't large enough you can't recover.
You can specify specific character ranges for your strings, but it starts to look a little regexy, and the behavior of the "%[" format of scanf isn't mandated in the standard.
You can check for a third name, but the code looks unintuitive - it doesn't look like you only want two names, it looks like you want three. scanf also gives you very little control over how you handle whitespace.
EDIT: I initially thought from your question that the names were contained in brackets (a la "[Bruce] [Wayne]") but it now appears that was merely your convention for denoting a placeholder.
Anyway, despite my intense dislike of scanf, it has its uses. The biggest killer (for me) is the inability to distinguish between line endings and simple space separation. To fix that, you can call fgets to read the data into a buffer, then call sscanf on the buffer. This gives you both a) safer reading (scanf messes with the ability of other more straightforward functions to read from a buffer) and b) the benefits of scanf formats.
If you have to use scanf, your format basically be this:
" %s %s %s"
With the third being undesirable. As #Constantinius's answer shows, you'd need to read data into three buffers, and check whether or not the third passed. However, if you're reading multiple consecutive lines of this data, then the first entry of the next line would satisfy the third slot, falsely giving you an error. I highly recommend using fgets and sscanf or ditching the sscanf for more precise manual parsing in this case.
Here's a link to the fgets man page if you missed the one I snuck in earlier. If you decide to ditch sscanf, here are some other functions to look into: strchr (or strspn, or strcspn) to find how long the name is, strcpy or memcpy (but please not strncpy, it's not what you think it is) to copy data into the buffers.
I'm reading in a .txt file. I'm using fscanf to get the data as it is formatted.
The line I'm having problems with is this:
result = fscanf(fp, "%s", ap->name);
This is fine until I have a name with a whitespace eg: St Ives
So I use this to read in the white space:
result = fscanf(fp, "%[^\n]s", ap->name);
However, when I try to read in the first name (with no white space) it just doesn't work and messes up the other fscanf.
But I use the [^\n] it works fine within a different file I'm using. Not sure what is happening.
If I use fgets in the place of the fscanf above I get "\n" in the variable.
Edit//
Ok, so if I use:
result = fscanf(fp, "%s", ap->name);
result = fscanf(fp, "%[^\n]s", ap->name);
This allows me to read in a string with no white space. But When I get a "name" with whitespace it doesn't work.
One problem with this:
result = fscanf(fp, "%[^\n]s", ap->name);
is that you have an extra s at the end of your format specifier. The entire format specifier should just be %[^\n], which says "read in a string which consists of characters which are not newlines". The extra s is not part of the format specifier, so it's interpreted as a literal: "read the next character from the input; if it's an "s", continue, otherwise fail."
The extra s doesn't actually hurt you, though. You know exactly what the next character of input: a newline. It doesn't match, and input processing stops there, but it doesn't really matter since it's the end of your format specifier. This would cause problems, though, if you had other format specifiers after this one in the same format string.
The real problem is that you're not consuming the newline: you're only reading in all of the characters up to the newline, but not the newline itself. To fix that, you should do this:
result = fscanf(fp, "%[^\n]%*c", ap->name);
The %*c specifier says to read in a character (c), but don't assign it to any variable (*). If you omitted the *, you would have to pass fscanf() another parameter containing a pointer to a character (a char*), where it would then store the resulting character that it read in.
You could also use %[^\n]\n, but that would also read in any whitespace which followed the newline, which may not be what you want. When fscanf finds whitespace in its format specifier (a space, newline, or tab), it consumes as much whitespace as it can (i.e. you can think of it consuming the longest string that matches the regular expression [ \t\n]*).
Finally, you should also specify a maximum length to avoid buffer overruns. You can do this by placing the buffer length in between the % and the [. For example, if ap->name is a buffer of 256 characters, you should do this:
result = fscanf(fp, "%255[^\n]%*c", ap->name);
This works great for statically allocated arrays; unfortunately, if the array is dyamically sized at runtime, there's no easy to way to pass the buffer size to fscanf. You'll have to create the format string with sprintf, e.g.:
char format[256];
snprintf(format, sizeof(format), "%%%d[^\n]%%*c", buffer_size - 1);
result = fscanf(fp, format, ap->name);
Jumm wrote:
If I use fgets in the place of the fscanf above I get "\n" in the variable.
Which is a far easier problem to solve so go with it:
fgets( ap->name, MAX, fp ) ;
nlptr = strrchr ( ap->name, '\n' ) ;
if( nlptr != 0 )
{
*nlptr = '\0' ;
}
I'm not sure how you mean [^\n] is suppose to work. [] is a modifier which says "accept one character except any of the characters which is inside this block". The ^ inverts the condition. %s with fscanf only reads until it comes across a delimiter. For strings with spaces and newlines in them, use a combination of fgets and sscanf instead, and specify a restriction on the length.
There is no such thing as I gather you are trying to imply a regular expression in the fscanf function which does not exist, not that to my knowledge nor have I seen it anywhere - enlighten me on this.
The format specifier for reading a string is %s, it could be that you need to do it this way, %s\n which will pick up the newline.
But for pete's sake do not use the standard old gets family functions as specified by Clifford's answer above as that is where buffer overflows happen and was used in a infamous worm of the 1990's - the Morris Worm, more specifically in the fingerd daemon, that used to call gets that caused chaos. Fortunately, now, that has now been patched. And furthermore, a lot of programmers have been drilled into the mentality not to use the function.
Even Microsoft has adopted a safe version of gets family of functions, that specifies a parameter to indicate the length of buffer instead.
EDIT
My bad - I did not realize that Clifford indeed has specified the max length for input...Whoops! Sorry! Clifford's answer is correct! So +1 to Clifford's answer.
Thanks Neil for pointing out my error...
Hope this helps,
Best regards,
Tom.
I found the problem.
As Paul Tomblin said, I had an extra new line character in the field above. So using what tommieb75 said I used:
result = fscanf(fp, "%s\n", ap->code);
result = fscanf(fp, "%[^\n]s", ap->name);
And this fixed it!
Thanks for your help.