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
Related
I'm trying to read a line using the following code:
while(fscanf(f, "%[^\n\r]s", cLine) != EOF )
{
/* do something with cLine */
}
But somehow I get only the first line every time. Is this a bad way to read a line? What should I fix to make it work as expected?
It's almost always a bad idea to use the fscanf() function as it can leave your file pointer in an unknown location on failure.
I prefer to use fgets() to get each line in and then sscanf() that. You can then continue to examine the line read in as you see fit. Something like:
#define LINESZ 1024
char buff[LINESZ];
FILE *fin = fopen ("infile.txt", "r");
if (fin != NULL) {
while (fgets (buff, LINESZ, fin)) {
/* Process buff here. */
}
fclose (fin);
}
fgets() appears to be what you're trying to do, reading in a string until you encounter a newline character.
If you want read a file line by line (Here, line separator == '\n') just make that:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
FILE *fp;
char *buffer;
int ret;
// Open a file ("test.txt")
if ((fp = fopen("test.txt", "r")) == NULL) {
fprintf(stdout, "Error: Can't open file !\n");
return -1;
}
// Alloc buffer size (Set your max line size)
buffer = malloc(sizeof(char) * 4096);
while(!feof(fp))
{
// Clean buffer
memset(buffer, 0, 4096);
// Read a line
ret = fscanf(fp, "%4095[^\n]\n", buffer);
if (ret != EOF) {
// Print line
fprintf(stdout, "%s\n", buffer);
}
}
// Free buffer
free(buffer);
// Close file
fclose(fp);
return 0;
}
Enjoy :)
If you try while( fscanf( f, "%27[^\n\r]", cLine ) == 1 ) you might have a little more luck. The three changes from your original:
length-limit what gets read in - I've used 27 here as an example, and unfortunately the scanf() family require the field width literally in the format string and can't use the * mechanism that the printf() can for passing the value in
get rid of the s in the format string - %[ is the format specifier for "all characters matching or not matching a set", and the set is terminated by a ] on its own
compare the return value against the number of conversions you expect to happen (and for ease of management, ensure that number is 1)
That said, you'll get the same result with less pain by using fgets() to read in as much of a line as will fit in your buffer.
Using fscanf to read/tokenise a file always results in fragile code or pain and suffering. Reading a line, and tokenising or scanning that line is safe, and effective. It needs more lines of code - which means it takes longer to THINK about what you want to do (and you need to handle a finite input buffer size) - but after that life just stinks less.
Don't fight fscanf. Just don't use it. Ever.
It looks to me like you're trying to use regex operators in your fscanf string. The string [^\n\r] doesn't mean anything to fscanf, which is why your code doesn't work as expected.
Furthermore, fscanf() doesn't return EOF if the item doesn't match. Rather, it returns an integer that indicates the number of matches--which in your case is probably zero. EOF is only returned at the end of the stream or in case of an error. So what's happening in your case is that the first call to fscanf() reads all the way to the end of the file looking for a matching string, then returns 0 to let you know that no match was found. The second call then returns EOF because the entire file has been read.
Finally, note that the %s scanf format operator only captures to the next whitespace character, so you don't need to exclude \n or \r in any case.
Consult the fscanf documentation for more information: http://www.cplusplus.com/reference/clibrary/cstdio/fscanf/
Your loop has several issues. You wrote:
while( fscanf( f, "%[^\n\r]s", cLine ) != EOF )
/* do something */;
Some things to consider:
fscanf() returns the number of items stored. It can return EOF if it reads past the end of file or if the file handle has an error. You need to distinguish a valid return of zero in which case there is no new content in the buffer cLine from a successfully read.
You do a have a problem when a failure to match occurs because it is difficult to predict where the file handle is now pointing in the stream. This makes recovery from a failed match harder to do than might be expected.
The pattern you wrote probably doesn't do what you intended. It is matching any number of characters that are not CR or LF, and then expecting to find a literal s.
You haven't protected your buffer from an overflow. Any number of characters may be read from the file and written to the buffer, regardless of the size allocated to that buffer. This is an unfortunately common error, that in many cases can be exploited by an attacker to run arbitrary code of the attackers choosing.
Unless you specifically requested that f be opened in binary mode, line ending translation will happen in the library and you will generally never see CR characters, and usually not in text files.
You probably want a loop more like the following:
while(fgets(cLine, N_CLINE, f)) {
/* do something */ ;
}
where N_CLINE is the number of bytes available in the buffer starting a cLine.
The fgets() function is a much preferred way to read a line from a file. Its second parameter is the size of the buffer, and it reads up to 1 less than that size bytes from the file into the buffer. It always terminates the buffer with a nul character so that it can be safely passed to other C string functions.
It stops on the first of end of file, newline, or buffer_size-1 bytes read.
It leaves the newline character in the buffer, and that fact allows you to distinguish a single line longer than your buffer from a line shorter than the buffer.
It returns NULL if no bytes were copied due to end of file or an error, and the pointer to the buffer otherwise. You might want to use feof() and/or ferror() to distinguish those cases.
i think the problem with this code is because when you read with %[^\n\r]s, in fact, you reading until reach '\n' or '\r', but you don't reading the '\n' or '\r' also.
So you need to get this character before you read with fscanf again at loop.
Do something like that:
do{
fscanf(f, "%[^\n\r]s", cLine) != EOF
/* Do something here */
}while(fgetc(file) != EOF)
I want to read some string input from the user and write it to a file. Right now I'm doing
char name[25];
scanf("%s", name);
int handle = open("./visitors.txt", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
if (handle < 0){
printf("File error.\n");
return;
}
lseek(handle, -sizeof(name), SEEK_END);
write(handle, name, sizeof(name));
This, of course is not right, since most of the time the user doesn't write 25 characters, only less, so when the user inputs 5 characters, the other 20 will be empty, and I end up having 5 chars the user put in and 20 chars gibberish in my output file. How can I make sure only the user input is being written to the file?
You are using raw system calls to handle the file operations. Unless your intention is specifically to learn about system calls, you should not do that. The write call writes a number of bytes specified by the third parameter. sizeof(name) is wrong in this case. It will not return the length of the string, it will return the length of the entire buffer.
The standard strlen function gives you the string length. So running strlen(name) will give you 5 if the user enters 5 characters, unlike the sizeof you are using.
Unless you want to learn about system calls specifically, you should probably handle file operations with the C standard library, using fprintf to output to file, and functions such as fopen and fclose. Take a look at the fprintf docs that also link to the other functions.
I'm not sure the reason you want to use low level system calls open, lseek, write. Usually it's a lot more convenient to use standard C fopen, fscanf, fprintf functions for such tasks.
In this case, you may try fgets to get string input from stdin, then use fprintf with %s to write to file. You can still use buffer name[25] or name[100] (envision for largest input from stdin). Just initialise it properly to make sure it's NULL terminated. Then fprintf with %s would write the string properly (ie without gibberish ending) to file.
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().
#include <stdlib.h>
#include <stdio.h>
int main() {
char ch, file_name[25];
FILE *fp;
printf("Enter the name of file you wish to see\n");
gets(file_name);
fp = fopen(file_name,"r"); // is for read mode
if (fp == NULL) {
printf(stderr, "There was an Error while opening the file.\n");
return (-1);
}
printf("The contents of %s file are :\n", file_name);
while ((ch = fgetc(fp)) != EOF)
printf("%c",ch);
fclose(fp);
return 0;
}
This code seems to work but I keep getting a warning stating "warning: this program uses gets(), which is unsafe."
So I tried to use fgets() but I get an error which states "too few arguments to function call expected 3".
Is there a way around this?
First : Never use gets() .. it can cause buffer overflows
second: show us how you used fgets() .. the correct way should look something like this:
fgets(file_name,sizeof(file_name),fp); // if fp has been opened
fgets(file_name,sizeof(file_name),stdin); // if you want to input the file name on the terminal
// argument 1 -> name of the array which will store the value
// argument 2 -> size of the input you want to take ( size of the input array to protect against buffer overflow )
// argument 3 -> input source
FYI:
fgets converts the whole input into a string by putting a \0 character at the end ..
If there was enough space then fgets will also get the \n from your input (stdin) .. to get rid of the \n and still make the whole input as a string , do this:
fgets(file_name,sizeof(file_name),stdin);
file_name[strlen(file_name)] = '\0';
Yes: fgets expects 3 arguments: the buffer (same as with gets), the size of the buffer and the stream to read from. In your case your buffer-size can be obtained with sizeof file_name and the stream you want to read from is stdin. All in all, this is how you'll call it:
fgets(file_name, sizeof file_name, stdin);
The reason gets is unsafe is because it doesn't (cannot) know the size of the buffer that it will read into. Therefore it is prone to buffer-overflows because it will just keep on writing to the buffer even though it's full.
fgets doesn't have this problem because it makes you provide the size of the buffer.
ADDIT: your call to printf inside the if( fp == NULL ) is invalid. printf expects as its first argument the format, not the output stream. I think you want to call fprintf instead.
Finally, in order to correctly detect EOF in your while-condition you must declare ch as an int. EOF may not necessarily fit into a char, but it will fit in an int (and getc also returns an int). You can still print it with %c.
Rather than ask how to use fgets() you should either use google, or look at the Unix/Linux man page or the VisualStudio documentation for the function. There are hundreds of functions in C, C++ and lots of class objects. You need to first figure out how to answer the basics yourself, so that your real questions stand a chance of being answered.
If you are new to C, you are definitely doing the right thing of experimenting, but take a look at other code, as you go along, to learn some of the tips/tricks of how code is written.
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.)