This question already has answers here:
How can I read an input string of unknown length?
(11 answers)
Closed 4 years ago.
I want to read in an arbitrary number of strings, one at a time, using <stdio.h> in C.
I know that you can do this with integers using:
while (scanf("%d ", &integer))
but I cannot do:
while (scanf("%s", string))
How can I implement the above?
The input is on separate lines.
You usually want to use fgets to read input as strings, especially when you want one line of input to end up as one string.
You can also use fscanf with a scanset conversion to read a line at a time, such as:
char line[100], newline;
fscanf("%99[^\n]%c", line, &newline);
Then you can check whether newline=='\n' to determine whether you've read the entire line successfully, or the line was larger than the buffer you provided.
When you're trying to read line-oriented input, you normally want to avoid "%s" (even with a specified length) though, as this reads white-space delimited tokens, not entire lines.
Use a char array:
char charArray[100];
while (scanf("%s", &charArray))
I guess your problem is to terminate the loop. scanf returns the number of successful scanned elements. In case of a string, also the empty string is successful scanned. Thus, you need another criterion, e.g.
while(scanf("%s",string) && (strlen(string)!=0))
I did not completely understand what you were trying to do from your original question. When you said you wanted to read in an arbitrary number of strings, I took that to mean, you wanted your program to be able to read 0 to n strings. Unfortunately in C, you will either have to cap off the maximum number of strings you would ever want to read in like
#define MAX_NUMBER_OF_STRINGS_TO_READ 25, or get into some sophisticated memory allocation scheme to read a string in and then add it to dynamic memory (returned from malloc).
I took the cap the maximum number of strings approach and wrote the following snippet:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char charArray[5][25] = {0};
int main(int argc, char *argv[])
{
int in_idx = 0;
int out_idx = 0;
printf("\n\n%s\n", "Enter no more than 5 strings, no more than 25 characters long.");
while(fgets (charArray[in_idx], 25, stdin))
{
if('\n' == charArray[in_idx][0])
{
printf("%s\n", "Entry terminated with newline.");
break;
}
in_idx++;
}
for(out_idx=0; out_idx < (in_idx + 1); out_idx++)
{
printf("%s", charArray[out_idx]);
}
printf("\n%s\n", "Program ended.");
return 0;
}
I made the termination character a newline. If I only want two strings, I press Enter when I've entered the second string. I terminated fgets by looking for a '\n' in the first position of the character array.
Related
This question already has answers here:
strcmp on a line read with fgets
(6 answers)
Closed 5 years ago.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void getSentence(char userSentence[]);
int breakSentence_andCompare(char userSentence[] , char compareSentence[]);
#define MAX_SENTENCE 100
int main()
{
int len = 0;
char userSentence[MAX_SENTENCE] = {'o','k',0};
char compareSentence[MAX_SENTENCE] = {'o',0};
getSentence(userSentence);
len = breakSentence_andCompare(userSentence,compareSentence);
}
/*
This function is asking the user to input info.
input:user input string array - char userSentence[].
output:none.
*/
void getSentence(char userSentence[])
{
printf("Hello And Welcome To The Palindrome Cheker Made By xXTH3 SKIRT CH4S3RXx");
printf("\nPlease Enter A Sentence: ");
fgets(userSentence,MAX_SENTENCE,stdin);
}
/*
This function takes the input of the user and input it into another string backwards.
input:user input string array - char userSentence[], backward user input string array - char compareSentence[].
output:strcmp value.
*/
int breakSentence_andCompare(char userSentence[] , char compareSentence[])
{
int i = 0;
int z = 0;
int len = 0;
int cmp = 0;
len = strlen(userSentence);
len -= 1;
for (i = len ; i >= 0 ; i--)
{
compareSentence[z] = userSentence[i];
printf("%s",compareSentence);
z++;
}
printf("\nuser: %s! compare: %s!",userSentence,compareSentence);
cmp = strcmp(userSentence,compareSentence);
printf("\n%d",&cmp);
return cmp;
}
This program checks if inputted string is palindrome,
To simply explain how it works:
It takes user input - String.
It Takes the user string and input is backwards in another string.
It compares the strings.
I have a really strange problem in the function and that's the strcmp return value. For some reason, when both strings have the same characters like ABBA the strcmp will return that the value of one of them is bigger. I'd really love to know what is the problem and how can I fix it.
P.S
When I were searching the problem I thought that it might be connected to fact that the user input string might contain \n from the enter key; is that possible?
And please understand that this isn't a whole code. The code is missing the output part.
The problem is occurring because of trailing \n (newline) character in user input string.
fgets()
The fgets() function shall read bytes from stream into the array
pointed to by s, until n-1 bytes are read, or a is read
and transferred to s, or an end-of-file condition is encountered.
The string is then terminated with a null byte. [EMPHASIS MINE]
So, a newline character makes fgets stop reading, but it is considered a valid character by the function and included in the input string.
Say, user input string is "ABBA". So, after entering the input the content of userSentence will be "ABBA\n".
In your program, you are reversing the input string and comparing it with the original input string.
After reversing, the content of compareSentence will be "\nABBA".
The content of both the strings are not same and the strcmp() will return non zero value as the comparison result.
There are various ways of removing trailing \n (newline) character from fgets() input. Check this.
Since you are a beginner, so one suggestion to you - Don't ignore the compiler warning('s).
For the statement -
printf("\n%d",&cmp);
You must be getting a warning message during compilation of your program.
You should try to identify the cause of warning message('s) and should resolve them.
There are much better ways to find out the whether a string is palindrome or not but since you are a beginner, your start is good. Happy Learning.. :)
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
So I'm trying to make it so that you can write text into a file until you make a newline or type -1. My problem is that when you write, it just keeps going until it crashes and gives the error "Stack around the variable "inputChoice" was corrupted".
I believe the problem is that the program doesn't stop accepting stdin when you want to stop typing (-1, newline) and that causes the error. I've tried with a simple scanf and it works, but you can only write a word. No spaces and it doesn't support multiple lines either. That's why I have to use fgets
Judging from your comments, I assume that there are some basic concepts in C
that you haven't fully understood, yet.
C-Strings
A C-String is a sequence of bytes. This sequence must end with the value 0.
Every value in the sequence represents a character based on the
ASCII encoding, for example the
character 'a' is 97, 'b' is 98, etc. The character '\0' has
the value 0 and it's the character that determines the end of the string.
That's why you hear a lot that C-Strings are '\0'-terminated.
In C you use an array of chars (char string[], char string[SOME VALUE]) to
save a string. For a string of length n, you need an array of dimension n+1, because
you also need one space for the terminating '\0' character.
When dealing with strings, you always have to think about the proper type,
whether your are using an array or a pointer. A pointer
to char doesn't necessarily mean that you are dealing with a C-String!
Why am I telling you this? Because of:
char inputChoice = 0;
printf("Do you wish to save the Input? (Y/N)\n");
scanf("%s", &inputChoice);
I haven't changed much, got very demotivated after trying for a while.
I changed the %s to an %c at scanf(" %c, &inputChoice) and that
seems to have stopped the program from crashing.
which shows that haven't understood the difference between %s and %c.
The %c conversion specifier character tells scanf that it must match a single character and it expects a pointer to char.
man scanf
c
Matches a sequence of characters whose length is specified by the maximum field
width (default 1); the next pointer must be a
pointer to char, and there must be enough room for all the characters
(no terminating null byte is added). The usual skip of
leading white space is suppressed. To skip white space first, use an explicit space in the format.
Forget the bit about the length, it's not important right now.
The important part is in bold. For the format scanf("%c", the function
expects a pointer to char and its not going to write the terminating '\0'
character, it won't be a C-String. If you want to read one letter and one
letter only:
char c;
scanf("%c", &c);
// also possible, but only the first char
// will have a defined value
char c[10];
scanf("%c", c);
The first one is easy to understand. The second one is more interesting: Here
you have an array of char of dimension 10 (i.e it holds 10 chars). scanf
will match a single letter and write it on c[0]. However the result won't be
a C-String, you cannot pass it to puts nor to other functions that expect
C-Strings (like strcpy).
The %s conversion specifier character tells scanf that it must match a sequence of non-white-space characters
man scanf
s
Matches a sequence of non-white-space characters; the next pointer must be a
pointer to the initial element of a character array that is long enough to
hold the input sequence and the terminating null byte ('\0'), which is added
automatically.
Here the result will be that a C-String is saved. You also have to have enough
space to save the string:
char string[10];
scanf("%s", string);
If the strings matches 9 or less characters, everything will be fine, because
for a string of length 9 requires 10 spaces (never forget the terminating
'\0'). If the string matches more than 9 characters, you won't have enough
space in the buffer and a buffer overflow (accessing beyond the size) occurs.
This is an undefined behaviour and anything can happen: your program might
crash, your program might not crash but overwrites another variable and thus
scrwes the flow of your program, it could even kill a kitten somewhere, do
you really want to kill kittens?
So, do you see why your code is wrong?
char inputChoice = 0;
scanf("%s", &inputChoice);
inputChoice is a char variable, it can only hold 1 value.
&inputChoice gives you the address of the inputChoice variable, but the
char after that is out of bound, if you read/write it, you will have an
overflow, thus you kill a kitten. Even if you enter only 1 character, it will
write at least 2 bytes and because you it only has space for one character, a kitten will die.
So, let's talk about your code.
From the perspective of an user: Why would I want to enter lines of text, possibly a lot of lines of text
and then answer "No, I don't want to save the lines". It doesn't make sense to
me.
In my opinion you should first ask the user whether he/she wants to save the
input first, and then ask for the input. If the user doesn't want to save
anything, then there is no point in asking the user to enter anything at
all. But that's just my opinion.
If you really want to stick to your plan, then you have to save every line and
when the user ends entering data, you ask and you save the file.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFERLEN 1024
void printFile () {
int i;
char openFile[BUFFERLEN];
FILE *file;
printf("What file do you wish to write in?\n");
scanf("%s", openFile);
getchar();
file = fopen(openFile, "w");
if (file == NULL) {
printf("Could not open file.\n");
return;
}
// we save here all lines to be saved
char **lines = NULL;
int num_of_lines = 0;
char buffer[BUFFERLEN];
printf("Enter an empty line of -1 to end input\n");
// for simplicity, we assume that no line will be
// larger than BUFFERLEN - 1 chars
while(fgets(buffer, sizeof buffer, stdin))
{
// we should check if the last character is \n,
// if not, buffer was not large enough for the line
// or the stream closed. For simplicity, I will ignore
// these cases
int len = strlen(buffer);
if(buffer[len - 1] == '\n')
buffer[len - 1] = '\0';
if(strcmp(buffer, "") == 0 || strcmp(buffer, "-1") == 0)
break; // either an empty line or user entered "-1"
char *line = strdup(buffer);
if(line == NULL)
break; // if no more memory
// process all lines that already have been entered
char **tmp = realloc(lines, (num_of_lines+1) * sizeof *tmp);
if(tmp == NULL)
{
free(line);
break; // same reason as for strdup failing
}
lines = tmp;
lines[num_of_lines++] = line; // save the line and increase num_of_lines
}
char inputChoice = 0;
printf("Do you wish to save the Input? (Y/N)\n");
scanf("%c", &inputChoice);
getchar();
if (inputChoice == 'Y' || inputChoice == 'y') {
for(i = 0; i < num_of_lines; ++i)
fprintf(file, "%s\n", lines[i]); // writing every line
printf("Your file has been saved\n");
printf("Please press any key to continue");
getchar();
}
// closing FILE buffer
fclose(file);
// free memory
if(num_of_lines)
{
for(i = 0; i < num_of_lines; ++i)
free(lines[i]);
free(lines);
}
}
int main(void)
{
printFile();
return 0;
}
Remarks on the code
I used the same code as yours as the base for mine, so that you can spot the
differences much quicker.
I use the macro BUFFERLEN for declaring the length of the buffers. That's
my style.
Look at the fgets line:
fgets(buffer, sizeof buffer, stdin)
I use here sizeof buffer instead of 1024 or BUFFERLEN. Again, that's my
style, but I think doing this is better, because even if you change the size
of the buffer by changing the macro, or by using another explicit size, sizeof buffer
will always return the correct size. Be aware that this only works when
buffer is an array.
The function strdup returns a pointer a pointer to a new string that
duplicates the argument. It's used to create a new copy of a string. When
using this function, don't forget that you have to free the memory using
free(). strdup is not part of the standard library, it conforms
to SVr4, 4.3BSD, POSIX.1-2001. If you use Windows (I don't use Windows,
I'm not familiar with the Windows ecosystem), this function might not be
present. In that case you can write your own:
char *strdup(const char *s)
{
char *str = malloc(strlen(s) + 1);
if(str == NULL)
return NULL;
strcpy(str, s);
return str;
}
I am trying to code a C program to start specific functions on the OS X El Capitan.
The code looks like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
char mainchoice;
printf(">>> ");
scanf("%s", &mainchoice);
if (strcmp(&mainchoice, "start ftp") == 0) {
system("ftp");
}
else if (strcmp(&mainchoice, "start say") == 0) {
system("say hello");
}
else {
system("say Error")
}
}
This is just a sample code.
When I run it, it always says error via the say command. What am I doing wrong?
Focus here:-
char mainchoice; //declared as a char
scanf("%s", &mainchoice); //using the %s placeholder which is for string
//for character it is %c
Getting the logic behind your code is you want to enter a String not a character.
Make an array of characters like this:-
char mainchoice[20]; //this can hold your string, one character at one index each of the array
Since, your are using multi word in string comparison("start say")
(strcmp(&mainchoice, "start say") == 0)
scanf does not work for multi words. scanf stops reading from the keyboard as soon as you provide a whitespace, tabs, newline.
For solving that problem, use fgets. It's the best way to read multi words or even whole sentences. Never use gets()! It is vulnerable to buffer overflow!
fgets(mainchoice, 20, stdin);
you are declaring mainchoice as a character rather then a string.
use char mainchoice[10]; to create a string.
and replace scanf("%s",&mainchoice) with fgets(mainchoice, 10, stdin);
size is 10 because you are comparing it with a string of length 9, so(9+1 null=10) 10 are enough.
I am currently learning C, and so I wanted to make a program that asks the user to input a string and to output the number of characters that were entered, the code compiles fine, when I enter just 1 character it does fine, but when I enter 2 or more characters, no matter what number of character I enter, it will always say there is just one character and crashes after that. This is my code and I can't figure out what is wrong.
int main(void)
{
int siz;
char i[] = "";
printf("Enter a string.\n");
scanf("%s", i);
siz = sizeof(i)/sizeof(char);
printf("%d", siz);
getch();
return 0;
}
I am currently learning to program, so if there is a way to do it using the same scanf() function I will appreciate that since I haven't learned how to use any other function and probably won't understand how it works.
Please, FORGET that scanf exists. The problem you are running into, whilst caused mostly by your understandable inexperience, will continue to BITE you even when you have experience - until you stop.
Here is why:
scanf will read the input, and put the result in the char buffer you provided. However, it will make no check to make sure there is enough space. If it needs more space than you provided, it will overwrite other memory locations - often with disastrous consequences.
A safer method uses fgets - this is a function that does broadly the same thing as scanf, but it will only read in as many characters as you created space for (or: as you say you created space for).
Other observation: sizeof can only evaluate the size known at compile time : the number of bytes taken by a primitive type (int, double, etc) or size of a fixed array (like int i[100];). It cannot be used to determine the size during the program (if the "size" is a thing that changes).
Your program would look like this:
#include <stdio.h>
#include <string.h>
#define BUFLEN 100 // your buffer length
int main(void) // <<< for correctness, include 'void'
{
int siz;
char i[BUFLEN]; // <<< now you have space for a 99 character string plus the '\0'
printf("Enter a string.\n");
fgets(i, BUFLEN, stdin); // read the input, copy the first BUFLEN characters to i
siz = sizeof(i)/sizeof(char); // it turns out that this will give you the answer BUFLEN
// probably not what you wanted. 'sizeof' gives size of array in
// this case, not size of string
// also not
siz = strlen(i) - 1; // strlen is a function that is declared in string.h
// it produces the string length
// subtract 1 if you don't want to count \n
printf("The string length is %d\n", siz); // don't just print the number, say what it is
// and end with a newline: \n
printf("hit <return> to exit program\n"); // tell user what to do next!
getc(stdin);
return 0;
}
I hope this helps.
update you asked the reasonable follow-up question: "how do I know the string was too long".
See this code snippet for inspiration:
#include <stdio.h>
#include <string.h>
#define N 50
int main(void) {
char a[N];
char *b;
printf("enter a string:\n");
b = fgets(a, N, stdin);
if(b == NULL) {
printf("an error occurred reading input!\n"); // can't think how this would happen...
return 0;
}
if (strlen(a) == N-1 && a[N-2] != '\n') { // used all space, didn't get to end of line
printf("string is too long!\n");
}
else {
printf("The string is %s which is %d characters long\n", a, strlen(a)-1); // all went according to plan
}
}
Remember that when you have space for N characters, the last character (at location N-1) must be a '\0' and since fgets includes the '\n' the largest string you can input is really N-2 characters long.
This line:
char i[] = "";
is equivalent to:
char i[1] = {'\0'};
The array i has only one element, the program crashes because of buffer overflow.
I suggest you using fgets() to replace scanf() like this:
#include <stdio.h>
#define MAX_LEN 1024
int main(void)
{
char line[MAX_LEN];
if (fgets(line, sizeof(line), stdin) != NULL)
printf("%zu\n", strlen(line) - 1);
return 0;
}
The length is decremented by 1 because fgets() would store the new line character at the end.
The problem is here:
char i[] = "";
You are essentially creating a char array with a size of 1 due to setting it equal to "";
Instead, use a buffer with a larger size:
char i[128]; /* You can also malloc space if you desire. */
scanf("%s", i);
See the link below to a similar question if you want to include spaces in your input string. There is also some good input there regarding scanf alternatives.
How do you allow spaces to be entered using scanf?
That's because char i[] = ""; is actually an one element array.
Strings in C are stored as the text which ends with \0 (char of value 0). You should use bigger buffer as others said, for example:
char i[100];
scanf("%s", i);
Then, when calculating length of this string you need to search for the \0 char.
int length = 0;
while (i[length] != '\0')
{
length++;
}
After running this code length contains length of the specified input.
You need to allocate space where it will put the input data. In your program, you can allocate space like:
char i[] = " ";
Which will be ok. But, using malloc is better. Check out the man pages.
Can anyone tell me why this code crashes? It's simple, if the length of the string is > than 16, ask again for a string. It works if I write control = 1 inside the if statement, but it should work the same without it, 'cause the value of control at that point is 1, am I right?
thans (I'm learning)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(void)
{
int control = 1;
char word[16] ;
printf("Enter a word: ");
while(control == 1)
{
scanf("%s", word);
int len = strlen(word);
printf("Lenght is: %d\n", len);
if (len >= 16)
{
printf("Word lenght to long, enter a new one: ");
}
else
{
control = 0;
}
}
printf("This is the word: %s\n", word );
}
char word[16] allocates 16 bytes of store for a string.
scanf() then reads a string into that store.
If you read in more than the amount of allocated store, memory is corrupted after the end of the store.
That's why you crash.
The problem is that if the user types more than the 15 characters which you have allocated space for, then the computer will merrily write all of them in memory past the end of your array. This will result in "undefined behavior" including crashing your program.
As others have noted, your fundamental problem is that you're allocating 16 characters for the string, and scanf will happily allow you to write past those 16 characters into memory that doesn't belong to you.
Be aware that C will allow you to do this with arrays generally, and understand how standard C strings work: you need to null-terminate them, meaning that you'll always need an extra space in the array for a null-terminating character \0.
There is a way to limit scanf with respect to C strings, using a field width specifier with %s, like so:
char input[17]; // room for 16 characters plus null-terminator
// here scanf will stop after reading 16 characters:
scanf("%16s", input);
With this code, you can safely use scanf to fill your string with no more than 16 characters, and scanf will null-terminate the string for you.
But as others have also noted, scanf is pretty poor at handling user input. It's usually better to use fgets and manage the input string on your own, piece-by-piece.