Adding Strings to a Text File in C - c

I am making a grocery list program and I want to include a user-input string that is put it in the next available line in the text file. Right now this adds the string to the file, but it then puts in random characters for a number of spaces and then, if I input something else, it won't be on the next line.
void AddToFile(FILE *a) {
char addItem[20];
printf("Enter item: ");
scanf("%s", &addItem);
fwrite(addItem, sizeof(addItem), 1, a);
}

this line should be:
// strlen instead of sizeof
fwrite(addItem, strlen(addItem), sizeof(char), a);
Your code always writes 20 characters, instead of the real number of characters that the string has.

Apart from the correction stated by pivotnig, fwrite() does not write a new line character to the file. Either write the new line after the fwrite() or append it to the addItem buffer before the fwrite().
You should prevent buffer overrun by limiting the number of characters copied to buf:
scanf("%19s", addItem);

In the current example, if you will try to write an item with more than 20 characters you will get into trouble as scanf will overwrite non allocated memory. A slightly improved version would be:
scanf("%19s",&addItem);
In this way, at least you will not overwrite random memory.
Edit: Thanks to Jack for pointing out \0 has to be written also.

Related

Skipping oversized inputs when using fgets(3)

this is probably quite easy to figure out, maybe i'm just looking in the wrong places, but how does one test if fgets has read an oversized input? In the code below, i'm trying to skip further processing for empty lines and oversized ones and go straight to the next line, for empty lines it works just fine.
Printing the strlen(buffer) when using line lengths < maxsize and it gives me expected values.
However when i enter lines that exceed the maxsize, it prints a value over 9000, which should still exceed the maxsize, and therefore enter the if-clause, but this doesn't happen. I've tried casting the return value of strlen into an int, didn't work.
What am i missing here? Thanks for any replies :)
char buffer[102];
while (fgets(buffer,100,stdin)!=NULL){
size_t maxsize = 102;
printf("%ld",strlen(buffer));
if(strcmp(buffer,"\n")==0||strlen(buffer)>maxsize){
continue;
}
//further processing
}
I
in the code:
char buffer[102];
while (fgets(buffer,100,stdin)!=NULL){
You don't need to give two more characters to buffer. The parameter size of fgets just can be the application of the sizeof operator, as in:
char buffer[102];
while (fgets(buffer, sizeof buffer, stdin) != NULL) {
That will give you space for lines of up to 101 characters (to leave space to the string terminator) including (or not, see below) the new line character.
But, answering your question, I understand that you want to know what happens if your input in one line is indeed bigger that the buffer size you provided, what happens then to the input, and how fgets deal with this:
Fgets() reads as many characters as it finds a \n in the input, or the buffer fills completely (this is, after including the \0 character that it must append to the string to terminate it) So, fgets() will fill as many characters in the buffer as the buffer has, minus one, reserved for the null string terminator, and the rest of the line will be read in the next fgets() (or another call to any of the functions of the stdio package).
So, basically, lines longer than one less than the buffer size are split in pieces, in which all except the last don't actually end in a new line, and the last will have the new line included, and will be shorter, all with a length of the length you specified minus one, but the last piece, in which the length is what it requires (again, always less than or equal than the length specified minus one)

I have two char arrays and when their sizes are different the program doesn't read/write them properly

I have a program to do basic things with two char arrays. Everything works fine when the size limit of the first is equal to size limit of the second, but when the size of the first char array is different to the size of the other, the program starts to read/write the strings in a strange way.
For example, if the limit of the first is 31 and the limit of the other is 5, if the typed characters in the first are more than 8 or something like that the program won't let the user typed anything on the second array as if it was full already.
I tried to fix it without using the functions of string.h, but the programs still did the same when the size limit of the two char arrays were different.
#include <stdio.h>
#include <string.h>
#define LIMIT1 31
#define LIMIT2 5
/*Function: void copy_string(char *pointer_destination, char *pointer_source)
Precondition: it needs a pointer to the direction of memory of the first element of two char vectors and the size limit of the 'destination' vector
Postcondition: it puts all the elements of the 'source' vector into the other until the last element that */
void copy_string(char *pointer_destination, char *pointer_source, int LIMd){
//Variable declaration
int i = 0;
/*Cycle for replacing the element of the 'destination' vector by the element of the 'source' vector.
When the element of the 'destination' OR of the 'source' is the null character, this cycle ends*/
for(; i < LIMd && *(pointer_source + i) != '\0'; i++){
*(pointer_destination + i) = *(pointer_source + i);
}
*(pointer_destination + i) = '\0';
}
int main(){
//Variable declaration
int restart;
char username[LIMIT1], string2[LIMIT2];//Here we define the limit for obvious reasons
//Restart cycle starts here
do{
//Data input
printf("Type your username (maximum 30 characters)\n");
fgets(username, LIMIT1 - 1, stdin);
fflush(stdin);
printf("Type a string of maximum 30 characters\n");
fgets(string2, LIMIT2 - 1, stdin);
fflush(stdin);
printf("Your typed username and your typed second string are, respectively:\n");
fputs(username, stdout);
fputs(string2, stdout);
printf("Concatenating, the username is now\n");
strcat(username, string2);
fputs(username, stdout);
printf("Now I'll copy what is in your username and I'll put it in the second string,\n");
copy_string(string2, username, LIMIT2 - 1);
fputs(string2, stdout);
//Restart cycle switch
printf("Type '0' to close this program, otherwise it'll restart itself\n");
scanf("%d", &restart);
fflush(stdin);
//Restart cycle ends here
}while(restart);
return 0;
}
I expected that if the size of the two arrays were different, the program would still read and write them properly (if the size of the first is 3, read from the user only the first three characters and put behing a \0 and if the size of the other is 25 do the same but with 25 as the size limit)
You're not very specific about your actual and expected output, but I imagine it's this:
Steps to reproduce:
Run the program as posted
Get the prompt Type your username (maximum 30 characters)
Enter this is an especially long string
Expected result:
A prompt that says Type a string of maximum 30 characters and lets you enter a new string
Actual result:
Type a string of maximum 30 characters is written to screen but the program continues immediately without letting you enter another string.
This happens because the first fgets is set up to read no more than 30 characters from the user. If you enter more, it will only consume the first 30.
The next fgets will then consume the remainder of that line instead of a new line, giving the appearance of skipping the prompt.
You should use a large enough buffer to accomodate the line, so that this is not an issue. Alternatively, you can manually read and discard one character at a time until you find a \n, effectively draining stdin of the rest of the line.
You seem to be relying on fflush(stdin) to clear any unread input. This is undefined behaviour in standard C, and only works on some platforms as a non-standard extension. I suspect it doesn't work on yours, and either breaks input altogether or does nothing and causes the next fgets to read the rest of the input intended for the previous one.
Instead of fflush, you can check whether the string read by fgets ends in a newline ('\n', which you probably want to remove if it is there). If not, keep reading (and discarding) input until either a newline '\n' or EOF is encountered.
(In general I would also recommend not using scanf for user input - it's a lot easier to read into a temporary buffer with fgets and parse that with sscanf as needed.)
Another obvious, but unrelated, problem is strcat(username, string2); – this may exceed the length of username. You need to leave at least LIMIT2 - 1 extra space (that you don't allow fgets to use), or simply allocate a new array of the correct size after you know the lengths of each.
Warning fgets will save the \n if there is enough place to save it, I think your problem comes because in your examples the end of line is saved at least in the usename. So you need to remove it if present.
Warning you give the size minus 1 to fgets, but fgets already read the given length minus 1 to have place to put the null character at the end.
Note the message to read the second string is wrong because it indicates a length 30 rather than 4.

How to print file contents to stdout without storing them in memory?

My program takes in files with arbitrarily long lines. Since I don't know how much characters would be on a line, I would like to print the whole line to stdout, without malloc-ing an array to store it. Is this possible?
I am aware that it's possible to print these lines one chunk at a time-- however, the function doing the printing would be called very often, and I wish to avoid the overhead of malloc-ing arrays that hold the output, in every single call.
First of all you can't print things that's not exist, means that you have to store it somewhere, either in the stack or heap. If you use FILE* then libc will do it for you automatically.
Now if you use FILE*, you can use getc to get an ASCII character a time, check if the character is a newline character and push it to stdout.
If you's using file descriptor, you can read a character a time and do exactly the same thing.
Both approaches does not require you explicitly allocate memory in the heap.
Now if you use mmap, you can perform some strtok family function and then print the string to stdout.
takes in files with arbitrarily long lines ... print the whole line to stdout, without malloc-ing an array to store it. Is this possible?
In general, for arbitrary long lines: no.
A text stream is an ordered sequence of characters composed into lines, each line consisting of zero or more characters plus a terminating new-line character. C11dr §7.21.2 2
The length of a line is not limited to SIZE_MAX, the longest array possible in C. The length of a line can exceed the memory capacity of the computer. There is just no way to read arbitrary long lines. Simply code could use the following. I doubt it will be satisfactory, yet it does print the entire contents of a file with scant memory.
// Reads one character at a time.
int ch;
while((ch = fgetc(fp)) != EOF) {
putchar(ch);
}
Instead, code should set a sane upper bound on line length. Create an array or allocate for the line. As much as a flexible long line is useful, it is also susceptible to malicious abuse by a hacker exploit consuming unrestrained resources.
#define LINE_LENGTH_MAX 100000
char *line = malloc(LINE_LENGTH_MAX + 1);
if (line) {
while (fgets(line, LINE_LENGTH_MAX+1, fp)) {
if (strlen(line) >= LINE_LENGTH_MAX) {
Handle_Possible_Attach();
}
foo(line); // Use line
}
free(line);
)

Reading the string with defined number of characters from the input

So I am trying to read a defined number of characters from the input. Let's say that I want to read 30 characters and put them in to a string. I managed to do this with a for loop, and I cleaned the buffer as shown below.
for(i=0;i<30;i++){
string[i]=getchar();
}
string[30]='\0';
while(c!='\n'){
c=getchar(); // c is some defined variable type char
}
And this is working for me, but I was wondering if there is another way to do this. I was researching and some of them are using sprintf() for this problem, but I didn't understand that solution. Then I found that you can use scanf with %s. And some of them use %3s when they want to read 3 characters. I tried this myself, but this command only reads the string till the first empty space. This is the code that I used:
scanf("%30s",string);
And when I run my program with this line, if I for example write: "Today is a beatiful day. It is raining, but it's okay i like rain." I thought that the first 30 characters would be saved in to the string. But when i try to read this string with puts(string); it only shows "Today".
If I use scanf("%s",string) or gets(string) that would rewrite some parts of my memory if the number of characters on input is greater than 30.
You can use scanf("%30[^\n]",s)
Actually, this is how you can set which characters to input. Here, carat sign '^' denotes negation, ie. this will input all characters except \n. %30 asks to input 30 characters. So, there you are.
The API you're looking for is fgets(). The man page describes
char *fgets(char *s, int size, FILE *stream);
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 ('\0') is stored after the last character in the buffer.

C - fgets() - length of newline char

I am trying to read 1 line and I am not sure how newline char is represented. Should I consider it as 2 chars or 1 char, when reading it from file by fgets() ? For example, I have a line of 15 chars + new line in file. So how should I safely allocate string and read that line?
At first, I tried this:
char buf[16];
fgets(buf, 16, f);
It read the line correctly without newline char and I assume that buf[15] holds the null character.
However, when I want to read and store the newline char, it doesn't work as I thought. As far as I know, '\n' should be considered as one char and take just one byte, so to read it, I just need to read one more char.
But when i try this
char buf[17];
fgets(buf, 17, f);
it does completely the same thing than previous example - there is now newline char stored in my string (I am not sure where null char is stored in this case)
To read entire line with newline I need to do this
char buf[18];
fgets(buf, 18, f);
OR this (it works, but I am not sure if it's safe)
char buf[17];
fgets(buf, 18, f);
So the questions is, why do I need to allocate and read 18 chars, when the line has only 15 chars + newline?
You need to provide buffer space for the 15-chars of text, up to 2 characters for the new line (to handle Windows line termination of \r\n), and one more for the null termination. So that's 18.
Like you did here:
char buf[18]; fgets(buf, 18, f);
The num parameter to fgets tells the call the size of your buffer it's writing to.
I am trying to read 1 line and I am not sure how newline char is represented.
In text mode, newline is '\n' and that's true on any conform C implementation and I wouldn't use fgets on anything but a text mode stream (I don't know -- and I don't want to know -- how it works in binary mode on an implementation using \r as end of line marker, or worse using an out of band end of line marker, I wouldn't be surprised it looks for a \n and never find one thus try to read until the end of file).
You should allocate space for the maximal line length, included the newline plus the terminating NUL and more important you must never lie the fgets about the length of the buffer. You can check if the buffer was long enough as the newline won't be present if it isn't.
The matter is about the espace sequence that lets you test for a newline, it is two characters \0x0d\0x0a but when using a strcmp and need to provide a string for this and a length, the C escape code holds in one character, so you must:
if(strncmp(&buff[i], "\n", 1) == 0)
which would not work with a length of two. Don't ask me why.

Resources