It would be better If I show you guys an example of what my program is supposed to do.
Input:
3
Double Double End
Triple Double End
Quadruple Double Triple End
Output:
4
6
24
So, the first sentence Double Double means 2*2 and Triple Double means 3*2 and so on.
The word End signifies the end of the string.
It looks very simple, but I have no idea how to work with strings and give them a value and continue on from there.
Here is all I have done so far:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int num_of_orders,i,j;
char orders[25];
char str1[25] = "Double";
char str2[25] = "Triple";
char str3[25] = "Quadruple";
scanf("%d", &num_of_orders);
for (i=0; i<num_of_orders+1; i++){
scanf("%s", orders);
}
return 0;
}
There are a number of ways to approach this problem, as indicated by the variety of answers. There is often no one right answer for how to approach a problem in C. The standard library provides a variety of tools that allow you to craft a number of solutions to just about any problem. As long as the code is correct and protects against error, then the choice of which approach to take largely boils down to a question of efficiency. For small bits of example code, that is rarely a consideration.
One approach to take is to recognize that you do not need the first line in your data file (except to read it/discard it to move the file-position-indicator to the start of the first line containing data.)
This allows you to simply use a line-oriented input function (fgets or getline) to read the remaining lines in the file. strtok then provides a simple way to split each line into words (remembering to strip the '\n' or discard the last word in each line). Then it is a small matter of using strcmp to compare each word and multiply by the correct amount. Finally, output the product of the multiplication.
Here is one slightly different approach to the problem. The program will read from the filename given as the first argument (or from stdin by default):
#include <stdio.h>
#include <string.h>
enum { MAXC = 64 };
int main (int argc, char **argv) {
char buf[MAXC] = ""; /* line buffer */
char *delims = " \n"; /* delimiters */
int idx = 0; /* line index */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file pointer */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line */
if (!idx++) continue; /* discard line 1 */
char *p = buf;
size_t len = strlen (p); /* get length */
int prod = 1;
if (len && buf[len-1] == '\n') /* check for '\n' */
buf[--len] = 0; /* remove newline */
printf (" %s", buf); /* output buf before strtok */
/* tokenize line/separate on delims */
for (p = strtok (p, delims); p; p = strtok (NULL, delims))
{ /* make comparson and multiply product */
if (strcmp (p, "Double") == 0) prod *= 2;
if (strcmp (p, "Triple") == 0) prod *= 3;
if (strcmp (p, "Quadruple") == 0) prod *= 4;
}
printf (" = %d\n", prod); /* output product */
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
Use/Output
$ ./bin/dbltrpl <../dat/dbltrpl.txt
Double Double End = 4
Triple Double End = 6
Quadruple Double Triple End = 24
Look it over and let me know if you have questions.
When it comes to reading the input, you can use strtok with a " " as a parameter to delimite the words you're reading from the input. This is a function filling all of the words read on the input into an array of strings:
PARAMETERS:
char **words: array of strings where you will store all of the words read in the input
char *input: the input you read (i.e. "Double Double end")
char *s: the delimiter you'll use to read words in the input (i.e. " ", "\n")
void getWords(char **words, char *input, char *s){
*words = strtok(str, s);
while(*words){
words++;
*words = strtok(NULL, s);
}
words++;
*words=NULL; //last element will point to NULL
}
Once you have read the words from the input, and filled them inside an array of strings, you could do something like this to calculate the output:
int calculate(char **words){
int result = 1;
while(*words){
if (strcmp(*words, "Quadruple") == 0){
result *= 4;
}else if (strcmp(*words, "Triple") == 0){
result *= 3;
}else if (strcmp(*words, "Double") == 0){
result *= 2;
}else if (strcmp(*words, "End") == 0){
return result;
}
words++;
}
}
Note that you need to correctly initialize the parameters you're passing before calling those functions. Otherwise, it may cause a Segmentation Fault.
You will have to use the methods from the string.h library, such as: strcmp(to compare two strings), strcpy(to copy one string to another) etc. which are generally used when dealing with strings manipulation in c.
Since, we do not know the size of the results array at compile time, we will have to allocate memory to it dynamically. For this purpose I have used malloc and free.
Here is the code to do that:
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
int main()
{
int num_of_orders, i, j;
char orders[25];
char str1[25];
strcpy(str1,"Double");
char str2[25];
strcpy(str2,"Triple");
char str3[25];
strcpy(str3,"Quadruple");
scanf("%d", &num_of_orders);
getchar();
int *results = malloc(num_of_orders*sizeof(int));
for (i=0; i < num_of_orders; i++)
{
results[i] = 1;
strcpy(orders,"");
while(strcmp(orders,"End") != 0)
{
scanf("%s", orders);
getchar();
if(strcmp(orders,str1)==0)
results[i] *= 2;
else if(strcmp(orders,str2) == 0)
results[i] *= 3;
else if(strcmp(orders,str3)==0)
results[i] *= 4;
}
}
for(i = 0; i < num_of_orders; i++)
printf("%d\n", results[i]);
free(results);
return 0;
}
Note: This program uses strcmp, which does case-sensitive comparison. If you want case-insensitive comparison, then use strcasecmp instead.
Don't forget the fact that the multiplication of integers is commutative:
#include <stdio.h>
#include <string.h>
int main(void)
{
int num_of_orders, i;
char orders[25];
int result;
char *ptr;
scanf("%d", &num_of_orders);
getchar(); // To comsume '\n'
for (i = 0; i < num_of_orders; i++)
{
fgets(orders, sizeof orders, stdin);
result = 1;
ptr = orders;
while(ptr = strstr(ptr, "Double"))
{
result *= 2;
ptr++;
}
ptr = orders;
while(ptr = strstr(ptr, "Triple"))
{
result *= 3;
ptr++;
}
ptr = orders;
while(ptr = strstr(ptr, "Quadruple"))
{
result *= 4;
ptr++;
}
printf("%d\n", result);
}
return 0;
}
What a trivial approach!
Note that strtok() is destructive, namely it will modify order, which can cause some problems if you want to use it later. Also, I think programs using strtok() are less readable. So it might be better to avoid it when possible.
Related
I want to create a program in C that takes an arbitrary number of lines of arbitrary length as input and then prints to console the last line that was inputted. For example:
input:
hi
my name is
david
output: david
I figured the best way to do this would be to have a loop that takes each line as input and stores it in a char array, so at the end of the loop the last line ends up being what is stored in the char array and we can just print that.
I have only had one lecture in C so far so I think I just keep setting things up wrong with my Java/C++ mindset since I have more experience in those languages.
Here is what I have so far but I know that it's nowhere near correct:
#include <stdio.h>
int main()
{
printf("Enter some lines of strings: \n");
char line[50];
for(int i = 0; i < 10; i++){
line = getline(); //I know this is inproper syntax but I want to do something like this
}
printf("%s",line);
}
I also have i < 10 in the loop because I don't know how to find the total number of lines in the input which, would be the proper amount of times to loop this. Also, the input is being put in all at once from the
./program < test.txt
command in Unix shell, where test.txt has the input.
Use fgets():
while (fgets(line, sizeof line, stdin)) {
// don't need to do anything here
}
printf("%s", line);
You don't need a limit on the number of iterations. At the end of the file, fgets() returns NULL and doesn't modify the buffer, so line will still hold the last line that was read.
I'm assuming you know the maximum length of the input line.
This one here will surely do the job for you
static char *getLine( char * const b , size_t bsz ) {
return fgets(b, bsz, stdin) );
}
But remember fgets also puts a '\n' character at the end of buffer so perhaps something like this
static char *getLine( char * const b , size_t bsz ) {
if( fgets(b, bsz, stdin) ){
/* Optional code to strip NextLine */
size_t size = strlen(b);
if( size > 0 && b[size-1] == '\n' ) {
b[--size] = '\0';
}
/* End of Optional Code */
return b;
}
return NULL;
}
and your code needs to be altered a bit while calling the getline
#define BUF_SIZE 256
char line[BUF_SIZE];
for(int i = 0; i < 10; i++){
if( getLine(line, BUF_SIZE ) ) {
fprintf(stdout, "line : '%s'\n", line);
}
}
Now it is how ever quite possible to create function like
char *getLine();
but then one needs to define the behavior of that function for instance if the function getLine() allocates memory dynamically then you probably need use a free to de-allocate the pointer returned by getLine()
in which case the function may look like
char *getLine( size_t bsz ) {
char *b = malloc( bsz );
if( b && fgets(b, bsz, stdin) ){
return b;
}
return NULL;
}
depending on how small your function is you can entertain thoughts about making it inline perhaps that's a little off topic for now.
In order to have dynamic number of input of dynamic length, you have to keep on reallocating your buffer when the input is of greater length. In order to store the last line, you have to take another pointer to keep track of it and to stop the input from the terminal you have to press EOF key(ctrl+k). This should do your job.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *get_last_line(FILE* fp, size_t size){
//The size is extended by the input with the value of the provisional
char *str, *last_str = NULL;
int ch;
size_t len = 0, last_len = 0;
str = realloc(NULL, sizeof(char)*size);//size is start size
if(!str)return str;
while(ch=fgetc(fp)){
if(ch == EOF){
break;
}
if(ch == '\n'){
str[len]='\0';
last_len = len;
last_str = realloc(last_str,sizeof(char)*last_len);
last_str[last_len]='\0';
//storing the last line
memcpy(last_str,str,sizeof(char)*last_len);
str = realloc(NULL, sizeof(char)*size);//size is start size
len = 0;
}
else {
str[len++]=ch;
if(len==size){
str = realloc(str, sizeof(char)*(size+=16));
if(!str)return str;
}
}
}
free(str);
return last_str;
}
int main(void){
char *m;
printf("input strings : ");
m = get_last_line(stdin, 10);
printf("last string :");
printf("%s\n", m);
free(m);
return 0;
}
I wanted to know if there was a way to use scanf so I can take in an unknown number of string arguments and put them into a char* array. I have seen it being done with int values, but can't find a way for it to be done with char arrays. Also the arguments are entered on the same line separated by spaces.
Example:
user enters hello goodbye yes, hello gets stored in array[0], goodbye in array[1] and yes in array[2]. Or the user could just enter hello and then the only thing in the array would be hello.
I do not really have any code to post, as I have no real idea how to do this.
You can do something like, read until the "\n" :
scanf("%[^\n]",buffer);
you need to allocate before hand a big enough buffer.
Now go through the buffer count the number of words, and allocate the necessary space char **array = ....(dynamic string allocation), go to the buffer and copy string by string into the array.
An example:
int words = 1;
char buffer[128];
int result = scanf("%127[^\n]",buffer);
if(result > 0)
{
char **array;
for(int i = 0; buffer[i]!='\0'; i++)
{
if(buffer[i]==' ' || buffer[i]=='\n' || buffer[i]=='\t')
{
words++;
}
}
array = malloc(words * sizeof(char*));
// Using RoadRunner suggestion
array[0] = strtok (buffer," ");
for(int w = 1; w < words; w++)
{
array[w] = strtok (NULL," ");
}
}
As mention in the comments you should use (if you can) fgets instead fgets(buffer,128,stdin);.
More about strtok
If you have an upper bound to the number of strings you may receive from the user, and to the number of characters in each string, and all strings are entered on a single line, you can do this with the following steps:
read the full line with fgets(),
parse the line with sscanf() with a format string with the maximum number of %s conversion specifiers.
Here is an example for up to 10 strings, each up to 32 characters:
char buf[400];
char s[10][32 + 1];
int n = 0;
if (fgets(buf, sizeof buf, sdtin)) {
n = sscanf("%32s%32s%32s%32s%32s%32s%32s%32s%32s%32s",
s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9]));
}
// `n` contains the number of strings
// s[0], s[1]... contain the strings
If the maximum number is not known of if the maximum length of a single string is not fixed, or if the strings can be input on successive lines, you will need to iterate with a simple loop:
char buf[200];
char **s = NULL;
int n;
while (scanf("%199s", buf) == 1) {
char **s1 = realloc(s, (n + 1) * sizeof(*s));
if (s1 == NULL || (s1[n] = strdup(buf)) == NULL) {
printf("allocation error");
exit(1);
}
s = s1;
n++;
}
// `n` contains the number of strings
// s[0], s[1]... contain pointers to the strings
Aside from the error handling, this loop is comparable to the hard-coded example above but it still has a maximum length for each string. Unless you can use a scanf() extension to allocate the strings automatically (%as on GNU systems), the code will be more complicated to handle any number of strings with any possible length.
You can use:
fgets to read input from user. You have an easier time using this instead of scanf.
malloc to allocate memory for pointers on the heap. You can use a starting size, like in this example:
size_t currsize = 10
char **strings = malloc(currsize * sizeof(*strings)); /* always check
return value */
and when space is exceeded, then realloc more space as needed:
currsize *= 2;
strings = realloc(strings, currsize * sizeof(*strings)); /* always check
return value */
When finished using the requested memory from malloc() and realloc(), it's always to good to free the pointers at the end.
strtok to parse the input at every space. When copying over the char * pointer from strtok(), you must also allocate space for strings[i], using malloc() or strdup.
Here is an example I wrote a while ago which does something very similar to what you want:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define INITSIZE 10
#define BUFFSIZE 100
int
main(void) {
char **strings;
size_t currsize = INITSIZE, str_count = 0, slen;
char buffer[BUFFSIZE];
char *word;
const char *delim = " ";
int i;
/* Allocate initial space for array */
strings = malloc(currsize * sizeof(*strings));
if(!strings) {
printf("Issue allocating memory for array of strings.\n");
exit(EXIT_FAILURE);
}
printf("Enter some words(Press enter again to end): ");
while (fgets(buffer, BUFFSIZE, stdin) != NULL && strlen(buffer) > 1) {
/* grow array as needed */
if (currsize == str_count) {
currsize *= 2;
strings = realloc(strings, currsize * sizeof(*strings));
if(!strings) {
printf("Issue reallocating memory for array of strings.\n");
exit(EXIT_FAILURE);
}
}
/* Remove newline from fgets(), and check for buffer overflow */
slen = strlen(buffer);
if (slen > 0) {
if (buffer[slen-1] == '\n') {
buffer[slen-1] = '\0';
} else {
printf("Exceeded buffer length of %d.\n", BUFFSIZE);
exit(EXIT_FAILURE);
}
}
/* Parsing of words from stdin */
word = strtok(buffer, delim);
while (word != NULL) {
/* allocate space for one word, including nullbyte */
strings[str_count] = malloc(strlen(word)+1);
if (!strings[str_count]) {
printf("Issue allocating space for word.\n");
exit(EXIT_FAILURE);
}
/* copy strings into array */
strcpy(strings[str_count], word);
str_count++;
word = strtok(NULL, delim);
}
}
/* print and free strings */
printf("Your array of strings:\n");
for (i = 0; i < str_count; i++) {
printf("strings[%d] = %s\n", i, strings[i]);
free(strings[i]);
strings[i] = NULL;
}
free(strings);
strings = NULL;
return 0;
}
I have a csv file of double values ( 20 rows and 4 columns) that I want to read and store the values in a buffer to perform some operations. My following implementation gives me some characters on the screen. I tried to see where is the problem but I don't know where:
#include<stdio.h>
#include <stdlib.h>
#include<String.h>
int main()
{
char buff[80];
double buffer[80];
char *token = NULL;
FILE *fp = fopen("dataset.csv","r");
if(fp == NULL){
printf("File Reading ERROR!");
exit(0);
}
int c = 0;
do
{
fgets(buff, 80, fp);
token = strtok(buff,",");
while( token != NULL )
{
buffer[c] = (char) token;
token = strtok(NULL,",");
c++;
}
}while((getc(fp))!=EOF);
for(int i=1; i<=80; ++i){
printf("%c ", buff[i]);
if(i%4 == 0) printf("\n");
}
}
Any help is appreciated.
Nice attempt, modify it a bit, like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // not String.h
int main(void)
{
char buff[80];
double buffer[80] = {0}; // I like initialization my arrays. Could do for 'buff' too
char *token = NULL;
FILE *fp = fopen("dataset.csv","r");
if(fp == NULL){
printf("File Reading ERROR!");
exit(0);
}
int c = 0;
while(fgets(buff, 80, fp) != NULL) // do not use 'getc()' to control the loop, use 'fgets()'
{
// eat the trailing newline
buff[strlen(buff) - 1] = '\0';
token = strtok(buff, ",");
while( token != NULL )
{
// use 'atof()' to parse from string to double
buffer[c] = atof(token);
token = strtok(NULL,",");
c++;
}
}
// print as many numbers as you read, i.e. 'c' - 1
for(int i=1; i<=c - 1; ++i) // be consistent on where you place opening brackets!
{
printf("%f\n", buffer[i]);
}
// Usually, we return something at the end of main()
return 0;
}
Sample run:
C02QT2UBFVH6-lm:~ gsamaras$ cat dataset.csv
3.13,3.14,3.15,3.16
2.13,2.14,2.15,2.16
C02QT2UBFVH6-lm:~ gsamaras$ ./a.out
3.140000
3.150000
3.160000
2.130000
2.140000
2.150000
2.160000
Notes:
Use atof() to
parse from string to double in c.
We usually prefer fgets() over
getc().
You are typecasting token to (char). token is a character pointer - basically, a pointer to the string containing the next ,-delimited token. You need to parse the floating point number contained within that string to a double value, not typecast the string pointer itself to a char value. Try sscanf() for this.
When you output your values, you are outputting characters from the last input buffer, not the double values that you (attempt to) parse out of the input. Change your printf command to output double values (e.g. %f or %g) and pass it the values out of your buffer array of doubles, not the buff character array.
I've a program which takes any number of words from the command-line arguments and replaces them with the word 'CENSORED'. I finally have the program working for the first argument passed in, and I am having trouble getting the program to censor all arguments, outputted in just a single string. The program rather functions individually on a given argument and does not take them all into account. How would I modify this?
How does one use/manipulate multiple command-line arguments collectively ?
My code follows.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *replace_str(char *str, char *orig, char *rep, int j, int argc)
{
static char buffer[4096];
char *p;
for ( j = 1; j <= argc; j++ )
{
if(!(p = strstr(str, orig))) // Check if 'orig' is not in 'str'
{
if ( j == argc ) { return str; } // return str once final argument is reached
else { continue; } // restart loop with next argument
}
strncpy(buffer, str, p-str); // Copy characters from 'str' start to 'orig' str
buffer[p-str] = '\0';
if ( j == argc ) { return buffer; }
else { continue; }
}
sprintf(buffer+(p-str), "%s%s", rep, p+strlen(orig));
}
int main( int argc, char* argv[] ) //argv: list of arguments; array of char pointers //argc: # of arguments.
{
long unsigned int c, i = 0, j = 1;
char str[4096];
while ( (c = getchar()) != EOF )
{
str[i] = c; // save input string to variable 'str'
i++;
}
puts(replace_str( str, argv[j], "CENSORED", j, argc ) );
return 0;
}
i.e.
$ cat Hello.txt
Hello, I am me.
$ ./replace Hello me < Hello.txt
CENSORED, I am CENSORED.
Two issues, you are not guaranteeing a null-terminated str and second, you are not iterating over the words on the command line to censor each. Try the following in main after your getchar() loop:
/* null-terminate str */
str[i] = 0;
/* you must check each command line word (i.e. argv[j]) */
for (j = 1; j < argc; j++)
{
puts(replace_str( str, argv[j], "CENSORED", j, argc ) );
}
Note: that will place each of the CENSORED words on a separate line. As noted in the comments, move puts (or preferably printf) outside the loop to keep on a single line.
Edit
I apologize. You have more issues than stated above. Attempting to check the fix, it became apparent that you would continue to have difficulty parsing the words depending on the order the bad words were entered on the command line.
While it is possible to do the pointer arithmetic to copy/expand/contract the original string regardless of the order the words appear on the command line, it is far easier to simply separate the words provided into an array, and then compare each of the bad words against each word in the original string.
This can be accomplished relatively easily with strtok or strsep. I put together a quick example showing this approach. (note: make a copy of the string before passing to strtok, as it will alter the original). I believe this is what you were attempting to do, but you were stumbling on not having the ability to compare each word (thus your use of strstr to test for a match).
Look over the example and let me know if you have further questions. Note: I replaced your hardcoded 4096 with a SMAX define and provided a word max WMAX for words entered on the command line. Also always initialize your strings/buffers. It will enable you to always be able to easily find the last char in the buffer and ensure the buffer is always null-terminated.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define SMAX 4096
#define WMAX 50
char *replace_str (char *str, char **bad, char *rep)
{
static char buffer[SMAX] = {0};
char *p = buffer;
char *wp = NULL;
unsigned i = 0;
unsigned char censored = 0;
char *str2 = strdup (str); /* make copy of string for strtok */
char *savp = str2; /* and save start address to free */
if (!(wp = strtok (str2, " "))) /* get first word in string or bail */
{
if (savp) free (savp);
return str;
}
while (bad[i]) /* test against each bad word */
{
if (strcmp (wp, bad[i++]) == 0) /* if matched, copy rep to buffer */
{
memcpy (buffer, rep, strlen (rep));
censored = 1;
}
}
if (!censored) /* if no match, copy original word */
memcpy (buffer, wp, strlen (wp));
while ((wp = strtok (NULL, " "))) /* repeat for each word in str */
{
i = 0;
censored = 0;
memcpy (strchr (buffer, 0), " ", 1);
p = strchr (buffer, 0); /* (get address of null-term char) */
while (bad[i])
{
if (strcmp (wp, bad[i++]) == 0)
{
memcpy (p, rep, strlen (rep));
censored = 1;
}
}
if (!censored)
memcpy (p, wp, strlen (wp));
}
if (savp) free (savp); /* free copy of strtok string */
return buffer;
}
int main ( int argc, char** argv)
{
unsigned int i = 0;
char str[SMAX] = {0};
char *badwords[WMAX] = {0}; /* array to hold command line words */
for (i = 1; i < argc; i++) /* save command line in array */
badwords[i-1] = strdup (argv[i]);
i = 0; /* print out the censored words */
printf ("\nCensor words:");
while (badwords[i])
printf (" %s", badwords[i++]);
printf ("\n\n");
printf ("Enter string: "); /* promt to enter string to censor */
if (fgets (str, SMAX-1, stdin) == NULL)
{
fprintf (stderr, "error: failed to read str from stdin\n");
return 1;
}
str[strlen (str) - 1] = 0; /* strip linefeed from input str */
/* print out censored string */
printf ("\ncensored str: %s\n\n", replace_str (str, badwords, "CENSORED"));
i = 0; /* free all allocated memory */
while (badwords[i])
free (badwords[i++]);
return 0;
}
use/output
./bin/censorw bad realbad
Censor words: bad realbad
Enter string: It is not nice to say bad or realbad words.
censored str: It is not nice to say CENSORED or CENSORED words.
If I don't know how long the word is, I cannot write char m[6];,
The length of the word is maybe ten or twenty long.
How can I use scanf to get input from the keyboard?
#include <stdio.h>
int main(void)
{
char m[6];
printf("please input a string with length=5\n");
scanf("%s",&m);
printf("this is the string: %s\n", m);
return 0;
}
please input a string with length=5
input: hello
this is the string: hello
Enter while securing an area dynamically
E.G.
#include <stdio.h>
#include <stdlib.h>
char *inputString(FILE* fp, size_t size){
//The size is extended by the input with the value of the provisional
char *str;
int ch;
size_t len = 0;
str = realloc(NULL, sizeof(*str)*size);//size is start size
if(!str)return str;
while(EOF!=(ch=fgetc(fp)) && ch != '\n'){
str[len++]=ch;
if(len==size){
str = realloc(str, sizeof(*str)*(size+=16));
if(!str)return str;
}
}
str[len++]='\0';
return realloc(str, sizeof(*str)*len);
}
int main(void){
char *m;
printf("input string : ");
m = inputString(stdin, 10);
printf("%s\n", m);
free(m);
return 0;
}
With the computers of today, you can get away with allocating very large strings (hundreds of thousands of characters) while hardly making a dent in the computer's RAM usage. So I wouldn't worry too much.
However, in the old days, when memory was at a premium, the common practice was to read strings in chunks. fgets reads up to a maximum number of chars from the input, but leaves the rest of the input buffer intact, so you can read the rest from it however you like.
in this example, I read in chunks of 200 chars, but you can use whatever chunk size you want of course.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* readinput()
{
#define CHUNK 200
char* input = NULL;
char tempbuf[CHUNK];
size_t inputlen = 0, templen = 0;
do {
fgets(tempbuf, CHUNK, stdin);
templen = strlen(tempbuf);
input = realloc(input, inputlen+templen+1);
strcpy(input+inputlen, tempbuf);
inputlen += templen;
} while (templen==CHUNK-1 && tempbuf[CHUNK-2]!='\n');
return input;
}
int main()
{
char* result = readinput();
printf("And the result is [%s]\n", result);
free(result);
return 0;
}
Note that this is a simplified example with no error checking; in real life you will have to make sure the input is OK by verifying the return value of fgets.
Also note that at the end if the readinput routine, no bytes are wasted; the string has the exact memory size it needs to have.
I've seen only one simple way of reading an arbitrarily long string, but I've never used it. I think it goes like this:
char *m = NULL;
printf("please input a string\n");
scanf("%ms",&m);
if (m == NULL)
fprintf(stderr, "That string was too long!\n");
else
{
printf("this is the string %s\n",m);
/* ... any other use of m */
free(m);
}
The m between % and s tells scanf() to measure the string and allocate memory for it and copy the string into that, and to store the address of that allocated memory in the corresponding argument. Once you're done with it you have to free() it.
This isn't supported on every implementation of scanf(), though.
As others have pointed out, the easiest solution is to set a limit on the length of the input. If you still want to use scanf() then you can do so this way:
char m[100];
scanf("%99s",&m);
Note that the size of m[] must be at least one byte larger than the number between % and s.
If the string entered is longer than 99, then the remaining characters will wait to be read by another call or by the rest of the format string passed to scanf().
Generally scanf() is not recommended for handling user input. It's best applied to basic structured text files that were created by another application. Even then, you must be aware that the input might not be formatted as you expect, as somebody might have interfered with it to try to break your program.
There is a new function in C standard for getting a line without specifying its size. getline function allocates string with required size automatically so there is no need to guess about string's size. The following code demonstrate usage:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *line = NULL;
size_t len = 0;
ssize_t read;
while ((read = getline(&line, &len, stdin)) != -1) {
printf("Retrieved line of length %zu :\n", read);
printf("%s", line);
}
if (ferror(stdin)) {
/* handle error */
}
free(line);
return 0;
}
If I may suggest a safer approach:
Declare a buffer big enough to hold the string:
char user_input[255];
Get the user input in a safe way:
fgets(user_input, 255, stdin);
A safe way to get the input, the first argument being a pointer to a buffer where the input will be stored, the second the maximum input the function should read and the third is a pointer to the standard input - i.e. where the user input comes from.
Safety in particular comes from the second argument limiting how much will be read which prevents buffer overruns. Also, fgets takes care of null-terminating the processed string.
More info on that function here.
EDIT: If you need to do any formatting (e.g. convert a string to a number), you can use atoi once you have the input.
Safer and faster (doubling capacity) version:
char *readline(char *prompt) {
size_t size = 80;
char *str = malloc(sizeof(char) * size);
int c;
size_t len = 0;
printf("%s", prompt);
while (EOF != (c = getchar()) && c != '\r' && c != '\n') {
str[len++] = c;
if(len == size) str = realloc(str, sizeof(char) * (size *= 2));
}
str[len++]='\0';
return realloc(str, sizeof(char) * len);
}
Read directly into allocated space with fgets().
Special care is need to distinguish a successful read, end-of-file, input error and out-of memory. Proper memory management needed on EOF.
This method retains a line's '\n'.
#include <stdio.h>
#include <stdlib.h>
#define FGETS_ALLOC_N 128
char* fgets_alloc(FILE *istream) {
char* buf = NULL;
size_t size = 0;
size_t used = 0;
do {
size += FGETS_ALLOC_N;
char *buf_new = realloc(buf, size);
if (buf_new == NULL) {
// Out-of-memory
free(buf);
return NULL;
}
buf = buf_new;
if (fgets(&buf[used], (int) (size - used), istream) == NULL) {
// feof or ferror
if (used == 0 || ferror(istream)) {
free(buf);
buf = NULL;
}
return buf;
}
size_t length = strlen(&buf[used]);
if (length + 1 != size - used) break;
used += length;
} while (buf[used - 1] != '\n');
return buf;
}
Sample usage
int main(void) {
FILE *istream = stdin;
char *s;
while ((s = fgets_alloc(istream)) != NULL) {
printf("'%s'", s);
free(s);
fflush(stdout);
}
if (ferror(istream)) {
puts("Input error");
} else if (feof(istream)) {
puts("End of file");
} else {
puts("Out of memory");
}
return 0;
}
I know that I have arrived after 4 years and am too late but I think I have another way that someone can use. I had used getchar() Function like this:-
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//I had putten the main Function Bellow this function.
//d for asking string,f is pointer to the string pointer
void GetStr(char *d,char **f)
{
printf("%s",d);
for(int i =0;1;i++)
{
if(i)//I.e if i!=0
*f = (char*)realloc((*f),i+1);
else
*f = (char*)malloc(i+1);
(*f)[i]=getchar();
if((*f)[i] == '\n')
{
(*f)[i]= '\0';
break;
}
}
}
int main()
{
char *s =NULL;
GetStr("Enter the String:- ",&s);
printf("Your String:- %s \nAnd It's length:- %lu\n",s,(strlen(s)));
free(s);
}
here is the sample run for this program:-
Enter the String:- I am Using Linux Mint XFCE 18.2 , eclispe CDT and GCC7.2 compiler!!
Your String:- I am Using Linux Mint XFCE 18.2 , eclispe CDT and GCC7.2 compiler!!
And It's length:- 67
Take a character pointer to store required string.If you have some idea about possible size of string then use function
char *fgets (char *str, int size, FILE* file);
else you can allocate memory on runtime too using malloc() function which dynamically provides requested memory.
i also have a solution with standard inputs and outputs
#include<stdio.h>
#include<malloc.h>
int main()
{
char *str,ch;
int size=10,len=0;
str=realloc(NULL,sizeof(char)*size);
if(!str)return str;
while(EOF!=scanf("%c",&ch) && ch!="\n")
{
str[len++]=ch;
if(len==size)
{
str = realloc(str,sizeof(char)*(size+=10));
if(!str)return str;
}
}
str[len++]='\0';
printf("%s\n",str);
free(str);
}
I have a solution using standard libraries of C and also creating a string type (alias of char*) like in C++
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef char* string;
typedef struct __strstr {
char ch;
struct __strstr *next;
}Strstr;
void get_str(char **str) {
char ch, *buffer, a;
Strstr *new = NULL;
Strstr *head = NULL, *tmp = NULL;
int c = 0, k = 0;
while ((ch = getchar()) != '\n') {
new = malloc(sizeof(Strstr));
if(new == NULL) {
printf("\nError!\n");
exit(1);
}
new->ch = ch;
new->next = NULL;
new->next = head;
head = new;
}
tmp = head;
while (tmp != NULL) {
c++;
tmp = tmp->next;
}
if(c == 0) {
*str = "";
} else {
buffer = malloc(sizeof(char) * (c + 1));
*str = malloc(sizeof(char) * (c + 1));
if(buffer == NULL || *str == NULL) {
printf("\nError!\n");
exit(1);
}
tmp = head;
while (tmp != NULL) {
buffer[k] = tmp->ch;
k++;
tmp = tmp->next;
}
buffer[k] = '\0';
for (int i = 0, j = strlen(buffer)-1; i < j; i++, j--) {
a = buffer[i];
buffer[i] = buffer[j];
buffer[j] = a;
}
strcpy(*str, buffer);
// Dealloc
free(buffer);
while (head != NULL) {
tmp = head;
head = head->next;
free(tmp);
}
}
}
int main() {
string str;
printf("Enter text: ");
get_str(&str);
printf("%s\n", str);
return 0;
}