I face a strange issue while trying to get user input through my code. I am pretty sure that the issue is not with the code but related to OS like standard input stream (stdin) or something like that, but since I don't have another machine with similar OS setup (as it's practically impossible to find an SCO machine nowadays), I expect some programmatic workarounds to solve this. My program reads a stream of alphanumeric characters from the user terminated by a '\n'.
But no matter how I try this to achieve this by different means, it just accepts the initial 256 characters. Initially I suspected the issue is with the fgets function , but when I use try to read the same value from a file using fgets, its working as expected.
Approach 1:
main()
{
char szInLine[999];
memset(szInLine, 0, sizeof(szInLine));
fprintf(stdout, "\nPlease enter the encrypted value:\n");
if (fgets(szInLine, 997, stdin) == NULL)
return(1);
fprintf(stdout, "Encrypted data string contains %i characters: %s\n",
strlen(szInLine), szInLine);
}
Approach 2:
while(ch = getc(stdin)) != EOF)
{
if((*szInLine++ = ch) == '\n')
{
break;
}
}
*szInLine = '\0';
fprintf(stdout, "Encrypted data string contains %i characters: %s\n", strlen(szInLine), szInLine);
Output for both cases : "Encrypted data string contains 256 characters: abcde.....
Other approaches I already tried but didn't succeed include changing the data type of the buffer which holds the value (from string to unsigned long), dynamically allocating memory to the buffer, setting stdin as unbuffered e.t.c.
OS environment :
SCO Unix, 32bit
Compiler:
CC
See the ioctl() and stty() manual page on the SCO web site. You should be able to retrieve the difference in the settings by testing terminal vs. redirection.
well, your programs (both) have errors:
/* you should include <stdio.h> so fgets() can return a char *,
* If you don't, it's assumed fgets() returns an int value. */
#include <stdio.h>
main()
{
char szInLine[999];
memset(szInLine, 0, sizeof(szInLine)); /* you don't need this */
fprintf(stdout, "\nPlease enter the encrypted value:\n");
/* fgets accepts a buffer and its size, it will reserve space for
* one '\0' char. */
if (fgets(szInLine, sizeof szInLine, stdin) == NULL) {
/* it is good to print some diagnostic if you receive EOF */
return(1);
}
fprintf(stdout, "Encrypted data string contains %i characters: %s\n",
strlen(szInLine), szInLine);
/* you should return 0, here */
return(0);
}
The second is even worse:
/* unbalanced parenthesis, you lack a parenthesis after 'while' keyword */
while(ch = getc(stdin)) != EOF)
{
if((*szInLine++ = ch) == '\n')
{
break;
}
}
*szInLine = '\0';
/* if you move the pointer 'szInLine' it will always be pointing to the end of
* the string, so this printf will show 0 characters and an empty string, you
* had better to save the pointer at the beginning, so you don't lose the
* reference to the string beginning.
*/
fprintf(stdout, "Encrypted data string contains %i characters: %s\n", strlen(szInLine), szInLine);
This should work:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char buffer_in[1000];
char buffer_out[1000];
while (fgets(buffer_in, sizeof buffer, stdin)) {
/* you'll get a line of up to 'sizeof buffer_in - 1' chars with an
* ending '\n' (or a truncated if the line has more than 'sizeof
* buffer_in - 1' chars. Also, you'll have a '\n' at the end of the
* buffer, if the line filled partially the buffer. */
fprintf(stderr,
"String read (%d chars): %s", /* this is why I don't put a '\n' here */
strlen(buffer_in),
buffer_in);
/* encrypt(buffer_in, sizeof buffer_in, buffer_out, sizeof buffer_out); */
}
/* here you got EOF */
return 0;
}
or if you want to use getc():
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
/* it is very important that c be an int, see manual
* page of fgetc(), getch() or getchar() */
int c;
char buffer[1000], *p = buffer;
/* we check for buffer length and for EOF. As we are doing the hard
* work ourselves, we have to check for 'sizeof buffer - 1' to allow
* space for the '\0'. */
while ((p < buffer + sizeof buffer - 1) && ((c = getchar()) != EOF)) {
if (c == '\n') { /* A NEWLINE, act on buffer, and skip it. */
*p = '\0'; /* end the string */
printf("Read %d chars: %s\n", p - buffer, buffer);
/* crypt it ... */
/* ... */
p = buffer; /* reset buffer */
continue;
}
*p++ = c; /* add the character to the buffer */
}
/* here you got EOF */
return 0;
}
One final note:
Don't post snippets of code, but complete examples, as it is very difficult to identify which errors are mistakes on copying the code here, or which are mistakes you have made in the original program.
Related
I'm having some troubles using strtok function.
As an exercise I have to deal with a text file by ruling out white spaces, transforming initials into capital letters and printing no more than 20 characters in a line.
Here is a fragment of my code:
fgets(sentence, SIZE, f1_ptr);
char *tok_ptr = strtok(sentence, " \n"); //tokenazing each line read
tok_ptr[0] = toupper(tok_ptr[0]); //initials to capital letters
int num = 0, i;
while (!feof(f1_ptr)) {
while (tok_ptr != NULL) {
for (i = num; i < strlen(tok_ptr) + num; i++) {
if (i % 20 == 0 && i != 0) //maximum of 20 char per line
fputc('\n', stdout);
fputc(tok_ptr[i - num], stdout);
}
num = i;
tok_ptr = strtok(NULL, " \n");
if (tok_ptr != NULL)
tok_ptr[0] = toupper(tok_ptr[0]);
}
fgets(sentence, SIZE + 1, f1_ptr);
tok_ptr = strtok(sentence, " \n");
if (tok_ptr != NULL)
tok_ptr[0] = toupper(tok_ptr[0]);
}
The text is just a bunch of lines I just show as a reference:
Watch your thoughts ; they become words .
Watch your words ; they become actions .
Watch your actions ; they become habits .
Watch your habits ; they become character .
Watch your character ; it becomes your destiny .
Here is what I obtain in the end:
WatchYourThoughts;Th
eyBecomeWords.WatchY
ourWords;THeyBecomeA
ctions.WatchYourActi
ons;TheyBecomeHabits
.WatchYourHabits;The
yBecomeCharacteR.Wat
chYourCharacter;ItBe
comesYourDEstiny.Lao
-Tze
The final result is mostly correct, but sometimes (for example "they" in they become (and only in that case) or "destiny") words are not correctly tokenized. So for example "they" is split into "t" and "hey" resulting in THey (DEstiny in the other instance) after the manipulations I made.
Is it some bug or am I missing something? Probably my code is not that efficient and some condition may end up being critical...
Thank you for the help, it's not that big of a deal, I just don't understand why such a behaviour is occurring.
You have a large number of errors in your code and you are over-complicating the problem. The most pressing error is Why is while ( !feof (file) ) always wrong? Why? Trace the execution-path within your loop. You attempt to read with fgets(), and then you use sentence without knowing whether EOF was reached calling tok_ptr = strtok(sentence, " \n"); before you ever get around to checking feof(f1_ptr)
What happens when you actually reach EOF? That IS "Why while ( !feof (file) ) is always wrong?" Instead, you always want to control your read-loop with the return of the read function you are using, e.g. while (fgets(sentence, SIZE, f1_ptr) != NULL)
What is it you actually need your code to do?
The larger question is why are you over-complicating the problem with strtok, and arrays (and fgets() for that matter)? Think about what you need to do:
read each character in the file,
if it is whitespace, ignore it, set the in-word flag false,
if a non-whitespace, if 1st char in word, capitalize it, output the char, set the in-word flag true and increment the number of chars output to the current line, and finally
if it is the 20th character output, output a newline and reset the counter zero.
The bare-minimum tools you need from your C-toolbox are fgetc(), isspace() and toupper() from ctype.h, a counter for the number of characters output, and a flag to know if the character is the first non-whitespace character after a whitespace.
Implementing the logic
That makes the problem very simple. Read a character, is it whitespace?, set your in-word flag false, otherwise if your in-word flag is false, capitalize it, output the character, set your in-word flag true, increment your word count. Last thing you need to do is check if your character-count has reached the limit, if so output a '\n' and reset your character-count zero. Repeat until you run out of characters.
You can turn that into a code with something similar to the following:
#include <stdio.h>
#include <ctype.h>
#define CPL 20 /* chars per-line, if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
int c, in = 0, n = 0; /* char, in-word flag, no. of chars output in line */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while ((c = fgetc(fp)) != EOF) { /* read / validate each char in file */
if (isspace(c)) /* char is whitespace? */
in = 0; /* set in-word flag false */
else { /* otherwise, not whitespace */
putchar (in ? c : toupper(c)); /* output char, capitalize 1st in word */
in = 1; /* set in-word flag true */
n++; /* increment character count */
}
if (n == CPL) { /* CPL limit reached? */
putchar ('\n'); /* output newline */
n = 0; /* reset cpl counter */
}
}
putchar ('\n'); /* tidy up with newline */
if (fp != stdin) /* close file if not stdin */
fclose (fp);
}
Example Use/Output
Given your input file stored on my computer in dat/text220.txt, you can produce the output you are looking for with:
$ ./bin/text220 dat/text220.txt
WatchYourThoughts;Th
eyBecomeWords.WatchY
ourWords;TheyBecomeA
ctions.WatchYourActi
ons;TheyBecomeHabits
.WatchYourHabits;The
yBecomeCharacter.Wat
chYourCharacter;ItBe
comesYourDestiny.
(the executable for the code was compiled to bin/text220, I usually keep separate dat, obj, and bin directories for data, object files and executables to keep by source code directory clean)
note: by reading from stdin by default if no filename is provided as the first argument to the program, you can use your program to read input directly, e.g.
$ echo "my dog has fleas - bummer!" | ./bin/text220
MyDogHasFleas-Bummer
!
No fancy string functions required, just a loop, a character, a flag and a counter -- the rest is just arithmetic. It's always worth trying to boils your programming problems down to basic steps and then look around your C-toolbox and find the right tool for each basic step.
Using strtok
Don't get me wrong, there is nothing wrong with using strtok and it makes a fairly simple solution in this case -- the point I was making is that for simple character-oriented string-processing, it's often just a simple to loop over the characters in the line. You don't gain any efficiencies using fgets() with an array and strtok(), the read from the file is already placed into a buffer of BUFSIZ1.
If you did want to use strtok(), you should control you read-loop your with the return from fgets()and then you can tokenize with strtok() also checking its return at each point. A read-loop with fgets() and a tokenization loop with strtok(). Then you handle first-character capitalization and then limiting your output to 20-chars per-line.
You could do something like the following:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define CPL 20 /* chars per-line, if you need a constant, #define one (or more) */
#define MAXC 1024
#define DELIM " \t\r\n"
void putcharCPL (int c, int *n)
{
if (*n == CPL) { /* if n == limit */
putchar ('\n'); /* output '\n' */
*n = 0; /* reset value at mem address 0 */
}
putchar (c); /* output character */
(*n)++; /* increment value at mem address */
}
int main (int argc, char **argv) {
char line[MAXC]; /* buffer to hold each line */
int n = 0; /* no. of chars ouput in line */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (fgets (line, MAXC, fp)) /* read each line and tokenize line */
for (char *tok = strtok (line, DELIM); tok; tok = strtok (NULL, DELIM)) {
putcharCPL (toupper(*tok), &n); /* convert 1st char to upper */
for (int i = 1; tok[i]; i++) /* output rest unchanged */
putcharCPL (tok[i], &n);
}
putchar ('\n'); /* tidy up with newline */
if (fp != stdin) /* close file if not stdin */
fclose (fp);
}
(same output)
The putcharCPL() function is just a helper that checks if 20 characters have been output and if so outputs a '\n' and resets the counter. It then outputs the current character and increments the counter by one. A pointer to the counter is passed so it can be updated within the function making the updated value available back in main().
Look things over and let me know if you have further questions.
footnotes:
1. Depending on your version of gcc, the constant in the source setting the read-buffer size may be _IO_BUFSIZ. _IO_BUFSIZ was changed to BUFSIZ here: glibc commit 9964a14579e5eef9 For Linux BUFSIZE is defined as 8192 (512 on Windows).
This is actually a much more interesting OP from a professional point of view than some of the comments may suggest, despite the 'newcomer' aspect of the question, which may sometimes raise fairly deep, underestimated issues.
The fun thing is that on my platform (W10, MSYS2, gcc v.10.2), your code runs fine with correct results:
WatchYourThoughts;Th
eyBecomeWords.WatchY
ourWords;TheyBecomeA
ctions.WatchYourActi
ons;TheyBecomeHabits
.WatchYourHabits;The
yBecomeCharacter.Wat
chYourCharacter;ItBe
comesYourDestiny.
So first, congratulations, newcomer: your coding is not that bad.
This points to how different compilers may or may not protect against limited inappropriate coding or specification misuse, may or may not protect stacks or heaps.
This said, the comment by #Andrew Henle pointing to an illuminating answer about feof is quite relevant.
If you follow it and retrieve your feof test, just moving it down after read checks, not before (as below). Your code should yield better results (note: I will just alter your code minimally, deliberately ignoring lesser issues):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#define SIZE 100 // add some leeway to avoid off-by-one issues
int main()
{
FILE* f1_ptr = fopen("C:\\Users\\Public\\Dev\\test_strtok", "r");
if (! f1_ptr)
{
perror("Open issue");
exit(EXIT_FAILURE);
}
char sentence[SIZE] = {0};
if (NULL == fgets(sentence, SIZE, f1_ptr))
{
perror("fgets issue"); // implementation-dependent
exit(EXIT_FAILURE);
}
errno = 0;
char *tok_ptr = strtok(sentence, " \n"); //tokenizing each line read
if (tok_ptr == NULL || errno)
{
perror("first strtok parse issue");
exit(EXIT_FAILURE);
}
tok_ptr[0] = toupper(tok_ptr[0]); //initials to capital letters
int num = 0;
size_t i = 0;
while (1) {
while (1) {
for (i = num; i < strlen(tok_ptr) + num; i++) {
if (i % 20 == 0 && i != 0) //maximum of 20 char per line
fputc('\n', stdout);
fputc(tok_ptr[i - num], stdout);
}
num = i;
tok_ptr = strtok(NULL, " \n");
if (tok_ptr == NULL) break;
tok_ptr[0] = toupper(tok_ptr[0]);
}
if (NULL == fgets(sentence, SIZE, f1_ptr)) // let's get away whith annoying +1,
// we have enough headroom
{
if (feof(f1_ptr))
{
fprintf(stderr, "\n%s\n", "Found EOF");
break;
}
else
{
perror("Unexpected fgets issue in loop"); // implementation-dependent
exit(EXIT_FAILURE);
}
}
errno = 0;
tok_ptr = strtok(sentence, " \n");
if (tok_ptr == NULL)
{
if (errno)
{
perror("strtok issue in loop");
exit(EXIT_FAILURE);
}
break;
}
tok_ptr[0] = toupper(tok_ptr[0]);
}
return 0;
}
$ ./test
WatchYourThoughts;Th
eyBecomeWords.WatchY
ourWords;TheyBecomeA
ctions.WatchYourActi
ons;TheyBecomeHabits
.WatchYourHabits;The
yBecomeCharacter.Wat
chYourCharacter;ItBe
comesYourDestiny.
Found EOF
My goal is to read every line from a piped .txt file with the getline() function, but I somehow get a error every time I use this function:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
int Chars;
int size = 10;
char *string;
printf("Please enter a string: ");
string = (char*) malloc(size);
Chars = getline(&string, &size, stdin);
if (Chars == -1)
{
puts("ERROR!");
}
else
{
puts("You entered the following string: ");
puts(string);
printf("\nCurrent size for string block: %d", Chars);
}
return 0;
}
I always get the errorcode: [Error] Id retruned 1 exit status
I've reproduced the linking error on DevC++, in which getline() seems to be missing even after forcing recent C revisions with gcc compiler options such as -std=c11.
So I've rewritten your code using fgets():
char *fgets(char *s, int size, FILE *stream);
It is for sure more portable than getline but has a few differences:
It reads up to size-1 characters if the newline is not encountered before this limit (it automatically appends the string terminator). So it doesn't manage buffer reallocation
The resulting string contains the '\n' character, if found
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_STR_SIZE 32
int main( void )
{
int len = 0;
char *str;
printf("Please enter a string: ");
str = malloc(MAX_STR_SIZE); /* (1) */
while( 1 )
{
size_t newline_pos;
fgets( str, MAX_STR_SIZE, stdin );
/* (2) */
if( len == 0) /* (3) */
{
puts("You entered the following string: ");
}
newline_pos = strcspn(str, "\n" );
str[newline_pos] = '\0';
len += strlen(str); /* (4) */
fputs(str, stdout);
if(newline_pos < MAX_STR_SIZE-1) /* (5) */
break;
}
printf("\n\nCurrent size for string block: %d", len);
free( str ); /* (6) */
return 0;
}
So, basically, I just use fgets to read from stdin, iterating until the '\n' character is found. In order to understand is this condition is met, I use strcspn() function, and I use the same function to remove the newline from the resulting string.
A few notes/assumptions (check the corresponding number in code section):
Casting the result of malloc is required only if you are compiling with a C++ compiler. It can be omitted in C
Removed fgets error check: it returns NULL in case of error (no chars read before EOF is found. It won't happen reading from stdin)
Checking for len==0 we make sure that the "You entered the following string: " is printed only once
The length of the string is calculated by summing the length of the strings read in every iteration
The break condition is met when the string contains '\n'. Otherwise strcspn's return value will be MAX_STR_SIZE
Even if the OS will release all the dynamic memory used by the program, on return, it is a good habit always freeing it anyway
I am trying to input a file and have it be printed each character at a time but some characters are ignored.
I'm assuming that that's because they are non ascii chars and the fgets doesn't know what to do with them since the buffer is made of chars.
int main() {
while(1)
{
char str[50];
if (fgets(str, 50, stdin) == NULL)
{
exit(0);
}
for(int i = 0; str[i] != '\n' ; i++)
{
printf("%lc", str[i]);
}
printf("\n");
}
return 0;
}
I have a file with
ALICE’SE’E’E’E’E’E’
but my code outputs it as
ALICESEEEEEE
if you use fgets(), that will probably have some undefined behaviour with input characters like \0, as they are internally used by the string functions to mark the end of the data in a string.
fgets()is a text oriented function that reads input until it finds a new line character \n. It then places a \0 after it, so you know where the string ends.
But with binary data, you can get control characters, even null characters in the middle of the data stream, making sometimes characters to dissapear on output (because they have been read, but your code doesn't go further when some of these are encountered later on writing).
If you want to allow all binary characters, you have several approaches here:
Use the binary stream counterparts from stdio: fread(3) and fwrite(3) functions allow you to read binary data as well as text:
#include <stdio.h>
#include <stdlib.h> /* for EXIT_* constants and exit() */
#define N (8192) /* buffer size (guessed, probably not optimum) */
int main()
{
ssize_t n;
char buffer[N];
while((n = fread(buffer, sizeof buffer[0], N, stdin)) > 0) {
ssize_t nout = fwrite(buffer, sizeof buffer[i], n, stdout);
if (nout != n) { /* error */
fprintf(stderr, "Error writing stdout\n");
exit(EXIT_FAILURE);
}
}
exit(EXIT_SUCCESS);
} /* main */
Use the simple Kernighan & Ritchie's sample from "The C programming language" book (I've added some error processing code):
#include <stdio.h>
#include <stdlib.h> /* idem. */
int main()
{
while((c = fgetc(stdin)) != EOF)
if (fputc(stdout) == EOF) {
fprintf("fputc error\n");
exit(EXIT_FAILURE);
}
}
if (ferror(stdin)) {
fprintf("fgetc error\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
} /* main */
or use the standard UNIX system calls:
#include <unistd.h> /* for prototypes for read()/write() syscalls */
#include <stdio.h>
#include <stdlib.h>
#define N (8192) /* guessed buffer size */
int main()
{
char buffer[N];
ssize_t n;
while ((n = read(0, buffer, sizeof buffer)) > 0) {
ssize_t nout;
nout = write(1, buffer, sizeof buffer));
if (nout != n) {
fprintf(stderr, "write: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
}
if (n < 0) {
fprintf(stderr, "read: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
} /* main */
but beware that probably the most efficient code you'll get can be the original character oriented sample from the K&R book, as stdio will select an optimum buffer size that will make it run faster, despite of the higher number of loop executions.
note
Anyway, your output will be far to be what you want, as some control characters are not output to the terminal, but interpreted as control characters (most popular being \n, which makes the terminal to continue on the next line) You have also to deal with this.
Even if you read multibyte characters as single byte, you can process those with the examples given, as a character that uses two bytes, will be read as two, but on printing, those will become the single char the terminal should display. As long as you apply no transformation to the data flow, there will be no difference in output with the sample code snippets you have above this.
You are using fgets, which deals with chars, and a char array (char str[50]).
But a printf formatter %lc which is for wide char.
If you want to input wide char, you need to use fgetws, and an array of type( wchar_t str[50])
I am learning getting inputs from key board. I want the user create a or more strings from the input, each string is considered as a line, the program will not terminate until a specified char is pressed. Then store these strings to the buffer.
However, when I print out the buffer, the first few elements of the string are always missing. Here is my code:
#include<stdio.h>
int main(void){
printf("Please type the string:\n");
char buffer[1000];
int c;
while( (c = getchar()) != ' ' ) {
fgets(buffer, sizeof(buffer), stdin);
printf("The output string is: \n%s\n", buffer);
if((c = getchar())== ' '){
printf("A space is detected!\n");
break;
}
}
}
The output is:
Please type the string:
abcdefg
The output string is:
bcdefg
hijklmn
The output string is:
jklmn
opqrst
The output string is:
qrst
A space is detected!
Program ended with exit code: 0
Which part did I go wrong? Any hints are very much appreciated.
The problem you are having is both getchar(), and fgets in your code are reading from stdin. Since you call getchar() first in your test, it was consuming the first character of your string, when you called it again, another character disappeared...
You don't need getchar() at all to end your loop. All you care about for breaking your loop as you have explained is whether the user enters a space as the first character. fgets does not skip leading whitespace, so any leading space entered by the user will be captured at the beginning of buffer. So to satisfy your loop-exit condition, all you need to do is check if the first character of buffer is a space.
How? The simple way is to just derererence buffer, e.g. *buffer returns the first character in buffer. How? In pointer notation, buffer + 0 is the offset you want in buffer, so to get the character at that location, you dereference, e.g. *(buffer + 0), which of course is just *buffer, which is the equivalent of buffer[0].
So, putting it altogether, and getting rid of getchar(), and adding strlen to properly validate that the string fit in buffer and to get the location for the trailing '\n' read and included in buffer by fgets (which leaves you with the length of trimmed string as a benefit), you could do something similar to:
#include <stdio.h>
#include <string.h>
#define MAXC 1000 /* if you need a constant, define one (or more) */
int main (void) {
char buffer[MAXC] = ""; /* initialize strings zero (good practice) */
for (;;) { /* loop continually taking input */
size_t len; /* variable for buffer length */
printf ("\nenter string: "); /* prompt */
if (!fgets (buffer, sizeof buffer, stdin)) /* read input */
break; /* exit if user cancels input */
len = strlen (buffer); /* get length */
if (len && buffer[len-1] == '\n') /* check if last char is \n */
buffer[--len] = 0; /* overwrite with nul-char */
else { /* otherwise string too long */
fputs ("error: string too long.\n", stderr);
return 1;
}
if (*buffer == ' ') /* check if 1st char of buffer is ' ' */
break;
printf ("buffer: %s (%zu chars)\n", buffer, len); /* output */
}
}
Example Use/Output
$ ./bin/fgetsspace
enter string: my dog has fleas
buffer: my dog has fleas (16 chars)
enter string: my cat has none
buffer: my cat has none (15 chars)
enter string: bye
(note: a space was entered before bye above, e.g. " bye")
Look things over and let me know if you have further questions.
Separating Words with strtok
To separate each line into individual words you can use strtok. The first argument is the buffer (for the 1st call), the second parameter is a list of characters to use as delimeters between the words (e.g. if you want to separate on space include a space, to not include the '.' at the end of a sentence include that as well -- and include the '\n'). After the 1st call to strtok all subsequent calls to get the remaining words uses NULL in place of buffer, e.g.
#include <stdio.h>
#include <string.h>
#define MAXC 1000 /* if you need a constant, define one (or more) */
int main (void) {
char buffer[MAXC] = ""; /* initialize strings zero (good practice) */
for (;;) { /* loop continually taking input */
size_t len; /* variable for buffer length */
char *delim = " .\n", /* delmiters for strtok */
*p = buffer; /* pointer to buffer for strtok */
printf ("\nenter string: "); /* prompt */
if (!fgets (buffer, sizeof buffer, stdin)) /* read input */
break; /* exit if user cancels input */
len = strlen (buffer); /* get length */
if (len && buffer[len-1] == '\n') /* check if last char is \n */
buffer[--len] = 0; /* overwrite with nul-char */
else { /* otherwise string too long */
fputs ("error: string too long.\n", stderr);
return 1;
}
if (*buffer == ' ') /* check if 1st char of buffer is ' ' */
break;
printf ("buffer: %s (%zu chars)\n", buffer, len); /* output */
p = strtok (buffer, delim); /* 1st call to strtok uses buffer */
while (p != NULL) {
printf (" %s\n", p);
p = strtok (NULL, delim); /* subsequent calls use NULL */
}
}
}
(note: the original buffer is modified, so make a copy if you need to preserve the original)
Example Use/Output
$ ./bin/fgetsspace
enter string: my dog has fleas
buffer: my dog has fleas (16 chars)
my
dog
has
fleas
enter string: my cat has none
buffer: my cat has none (15 chars)
my
cat
has
none
enter string: bye
getchar swallows up a character. Your first iteration gets one character swallowed up by the initial call in the while, and then successive iterations get two characters swallowed up, one by the getchar you use to detect a space and then again the one in the while.
Answering in addition to my initial comment and the issue:
First, quoting myself:
I believe that when using getChar(), you efficiently remove the character from stdin buffer.
As stated since then by other people, the problem is that your call to getchar function consume and input, efficiently removing it from stdin buffer.
See Jim Buck's answer for detailed informations on the precise behavior of your application.
Now, what should you do ?
First, the if inside the while loop is not necessary, and using your application right now must be pretty odd. Try doing :
#include<stdio.h>
int main(void){
printf("Please type the string:\n");
char buffer[1000];
int c;
while( (c = getchar()) != ' ' ) {
fgets(buffer, sizeof(buffer), stdin);
printf("The output string is: \n%s\n", buffer);
}
printf("A space is detected!\n");
}
Instead to prevent unnecessary user inputs. Your loop is basically an infinite loop so there is no need to check at the end of every iteration if the loop should terminate, the while statement is already doing that pretty damn well. :P
Now, to prevent the input from being taken out of buffer, I would consider using the buffer's first element instead of "c" variable.
Like so :
#include<stdio.h>
int main(void){
printf("Please type the strings:\n");
char buffer[1000];
while( (buffer[0] = getchar()) != ' ' ) { // Now reads directly into buffer
fgets(buffer + 1, sizeof(buffer), stdin); // + 1 prevents overriding the char we just read.
printf("The output string is: \n%s\n", buffer);
}
printf("A space is detected!\n");
}
Have a nice day!
What is the simplest way to read a full line in a C console program
The text entered might have a variable length and we can't make any assumption about its content.
You need dynamic memory management, and use the fgets function to read your line. However, there seems to be no way to see how many characters it read. So you use fgetc:
char * getline(void) {
char * line = malloc(100), * linep = line;
size_t lenmax = 100, len = lenmax;
int c;
if(line == NULL)
return NULL;
for(;;) {
c = fgetc(stdin);
if(c == EOF)
break;
if(--len == 0) {
len = lenmax;
char * linen = realloc(linep, lenmax *= 2);
if(linen == NULL) {
free(linep);
return NULL;
}
line = linen + (line - linep);
linep = linen;
}
if((*line++ = c) == '\n')
break;
}
*line = '\0';
return linep;
}
Note: Never use gets ! It does not do bounds checking and can overflow your buffer
If you are using the GNU C library or another POSIX-compliant library, you can use getline() and pass stdin to it for the file stream.
A very simple but unsafe implementation to read line for static allocation:
char line[1024];
scanf("%[^\n]", line);
A safer implementation, without the possibility of buffer overflow, but with the possibility of not reading the whole line, is:
char line[1024];
scanf("%1023[^\n]", line);
Not the 'difference by one' between the length specified declaring the variable and the length specified in the format string. It is a historical artefact.
So, if you were looking for command arguments, take a look at Tim's answer.
If you just want to read a line from console:
#include <stdio.h>
int main()
{
char string [256];
printf ("Insert your full address: ");
gets (string);
printf ("Your address is: %s\n",string);
return 0;
}
Yes, it is not secure, you can do buffer overrun, it does not check for end of file, it does not support encodings and a lot of other stuff.
Actually I didn't even think whether it did ANY of this stuff.
I agree I kinda screwed up :)
But...when I see a question like "How to read a line from the console in C?", I assume a person needs something simple, like gets() and not 100 lines of code like above.
Actually, I think, if you try to write those 100 lines of code in reality, you would do many more mistakes, than you would have done had you chosen gets ;)
getline runnable example
getline was mentioned on this answer but here is an example.
It is POSIX 7, allocates memory for us, and reuses the allocated buffer on a loop nicely.
Pointer newbs, read this: Why is the first argument of getline a pointer to pointer "char**" instead of "char*"?
main.c
#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char *line = NULL;
size_t len = 0;
ssize_t read = 0;
while (1) {
puts("enter a line");
read = getline(&line, &len, stdin);
if (read == -1)
break;
printf("line = %s", line);
printf("line length = %zu\n", read);
puts("");
}
free(line);
return 0;
}
Compile and run:
gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out
Outcome: this shows on therminal:
enter a line
Then if you type:
asdf
and press enter, this shows up:
line = asdf
line length = 5
followed by another:
enter a line
Or from a pipe to stdin:
printf 'asdf\nqwer\n' | ./main.out
gives:
enter a line
line = asdf
line length = 5
enter a line
line = qwer
line length = 5
enter a line
Tested on Ubuntu 20.04.
glibc implementation
No POSIX? Maybe you want to look at the glibc 2.23 implementation.
It resolves to getdelim, which is a simple POSIX superset of getline with an arbitrary line terminator.
It doubles the allocated memory whenever increase is needed, and looks thread-safe.
It requires some macro expansion, but you're unlikely to do much better.
You might need to use a character by character (getc()) loop to ensure you have no buffer overflows and don't truncate the input.
As suggested, you can use getchar() to read from the console until an end-of-line or an EOF is returned, building your own buffer. Growing buffer dynamically can occur if you are unable to set a reasonable maximum line size.
You can use also use fgets as a safe way to obtain a line as a C null-terminated string:
#include <stdio.h>
char line[1024]; /* Generously large value for most situations */
char *eof;
line[0] = '\0'; /* Ensure empty line if no input delivered */
line[sizeof(line)-1] = ~'\0'; /* Ensure no false-null at end of buffer */
eof = fgets(line, sizeof(line), stdin);
If you have exhausted the console input or if the operation failed for some reason, eof == NULL is returned and the line buffer might be unchanged (which is why setting the first char to '\0' is handy).
fgets will not overfill line[] and it will ensure that there is a null after the last-accepted character on a successful return.
If end-of-line was reached, the character preceding the terminating '\0' will be a '\n'.
If there is no terminating '\n' before the ending '\0' it may be that there is more data or that the next request will report end-of-file. You'll have to do another fgets to determine which is which. (In this regard, looping with getchar() is easier.)
In the (updated) example code above, if line[sizeof(line)-1] == '\0' after successful fgets, you know that the buffer was filled completely. If that position is proceeded by a '\n' you know you were lucky. Otherwise, there is either more data or an end-of-file up ahead in stdin. (When the buffer is not filled completely, you could still be at an end-of-file and there also might not be a '\n' at the end of the current line. Since you have to scan the string to find and/or eliminate any '\n' before the end of the string (the first '\0' in the buffer), I am inclined to prefer using getchar() in the first place.)
Do what you need to do to deal with there still being more line than the amount you read as the first chunk. The examples of dynamically-growing a buffer can be made to work with either getchar or fgets. There are some tricky edge cases to watch out for (like remembering to have the next input start storing at the position of the '\0' that ended the previous input before the buffer was extended).
How to read a line from the console in C?
Building your own function, is one of the ways that would help you to achieve reading a line from console
I'm using dynamic memory allocation to allocate the required amount of memory required
When we are about to exhaust the allocated memory, we try to double the size of memory
And here I'm using a loop to scan each character of the string one by one using the getchar() function until the user enters '\n' or EOF character
finally we remove any additionally allocated memory before returning the line
//the function to read lines of variable length
char* scan_line(char *line)
{
int ch; // as getchar() returns `int`
long capacity = 0; // capacity of the buffer
long length = 0; // maintains the length of the string
char *temp = NULL; // use additional pointer to perform allocations in order to avoid memory leaks
while ( ((ch = getchar()) != '\n') && (ch != EOF) )
{
if((length + 1) >= capacity)
{
// resetting capacity
if (capacity == 0)
capacity = 2; // some initial fixed length
else
capacity *= 2; // double the size
// try reallocating the memory
if( (temp = realloc(line, capacity * sizeof(char))) == NULL ) //allocating memory
{
printf("ERROR: unsuccessful allocation");
// return line; or you can exit
exit(1);
}
line = temp;
}
line[length] = (char) ch; //type casting `int` to `char`
length++;
}
line[length + 1] = '\0'; //inserting null character at the end
// remove additionally allocated memory
if( (temp = realloc(line, (length + 1) * sizeof(char))) == NULL )
{
printf("ERROR: unsuccessful allocation");
// return line; or you can exit
exit(1);
}
line = temp;
return line;
}
Now you could read a full line this way :
char *line = NULL;
line = scan_line(line);
Here's an example program using the scan_line() function :
#include <stdio.h>
#include <stdlib.h> //for dynamic allocation functions
char* scan_line(char *line)
{
..........
}
int main(void)
{
char *a = NULL;
a = scan_line(a); //function call to scan the line
printf("%s\n",a); //printing the scanned line
free(a); //don't forget to free the malloc'd pointer
}
sample input :
Twinkle Twinkle little star.... in the sky!
sample output :
Twinkle Twinkle little star.... in the sky!
I came across the same problem some time ago, this was my solutuion, hope it helps.
/*
* Initial size of the read buffer
*/
#define DEFAULT_BUFFER 1024
/*
* Standard boolean type definition
*/
typedef enum{ false = 0, true = 1 }bool;
/*
* Flags errors in pointer returning functions
*/
bool has_err = false;
/*
* Reads the next line of text from file and returns it.
* The line must be free()d afterwards.
*
* This function will segfault on binary data.
*/
char *readLine(FILE *file){
char *buffer = NULL;
char *tmp_buf = NULL;
bool line_read = false;
int iteration = 0;
int offset = 0;
if(file == NULL){
fprintf(stderr, "readLine: NULL file pointer passed!\n");
has_err = true;
return NULL;
}
while(!line_read){
if((tmp_buf = malloc(DEFAULT_BUFFER)) == NULL){
fprintf(stderr, "readLine: Unable to allocate temporary buffer!\n");
if(buffer != NULL)
free(buffer);
has_err = true;
return NULL;
}
if(fgets(tmp_buf, DEFAULT_BUFFER, file) == NULL){
free(tmp_buf);
break;
}
if(tmp_buf[strlen(tmp_buf) - 1] == '\n') /* we have an end of line */
line_read = true;
offset = DEFAULT_BUFFER * (iteration + 1);
if((buffer = realloc(buffer, offset)) == NULL){
fprintf(stderr, "readLine: Unable to reallocate buffer!\n");
free(tmp_buf);
has_err = true;
return NULL;
}
offset = DEFAULT_BUFFER * iteration - iteration;
if(memcpy(buffer + offset, tmp_buf, DEFAULT_BUFFER) == NULL){
fprintf(stderr, "readLine: Cannot copy to buffer\n");
free(tmp_buf);
if(buffer != NULL)
free(buffer);
has_err = true;
return NULL;
}
free(tmp_buf);
iteration++;
}
return buffer;
}
There is a simple regex like syntax that can be used inside scanf to take whole line as input
scanf("%[^\n]%*c", str);
^\n tells to take input until newline doesn't get encountered. Then, with %*c, it reads newline character and here used * indicates that this newline character is discarded.
Sample code
#include <stdio.h>
int main()
{
char S[101];
scanf("%[^\n]%*c", S);
printf("%s", S);
return 0;
}
On BSD systems and Android you can also use fgetln:
#include <stdio.h>
char *
fgetln(FILE *stream, size_t *len);
Like so:
size_t line_len;
const char *line = fgetln(stdin, &line_len);
The line is not null terminated and contains \n (or whatever your platform is using) in the end. It becomes invalid after the next I/O operation on stream.
Something like this:
unsigned int getConsoleInput(char **pStrBfr) //pass in pointer to char pointer, returns size of buffer
{
char * strbfr;
int c;
unsigned int i;
i = 0;
strbfr = (char*)malloc(sizeof(char));
if(strbfr==NULL) goto error;
while( (c = getchar()) != '\n' && c != EOF )
{
strbfr[i] = (char)c;
i++;
strbfr = (void*)realloc((void*)strbfr,sizeof(char)*(i+1));
//on realloc error, NULL is returned but original buffer is unchanged
//NOTE: the buffer WILL NOT be NULL terminated since last
//chracter came from console
if(strbfr==NULL) goto error;
}
strbfr[i] = '\0';
*pStrBfr = strbfr; //successfully returns pointer to NULL terminated buffer
return i + 1;
error:
*pStrBfr = strbfr;
return i + 1;
}
The best and simplest way to read a line from a console is using the getchar() function, whereby you will store one character at a time in an array.
{
char message[N]; /* character array for the message, you can always change the character length */
int i = 0; /* loop counter */
printf( "Enter a message: " );
message[i] = getchar(); /* get the first character */
while( message[i] != '\n' ){
message[++i] = getchar(); /* gets the next character */
}
printf( "Entered message is:" );
for( i = 0; i < N; i++ )
printf( "%c", message[i] );
return ( 0 );
}
Here is a minimal implementation to do it, the nice thing is that it will not keep the '\n', however you have to give it a size to read for security:
#include <stdio.h>
#include <errno.h>
int sc_gets(char *buf, int n)
{
int count = 0;
char c;
if (__glibc_unlikely(n <= 0))
return -1;
while (--n && (c = fgetc(stdin)) != '\n')
buf[count++] = c;
buf[count] = '\0';
return (count != 0 || errno != EAGAIN) ? count : -1;
}
Test with:
#define BUFF_SIZE 10
int main (void) {
char buff[BUFF_SIZE];
sc_gets(buff, sizeof(buff));
printf ("%s\n", buff);
return 0;
}
NB: You are limited to INT_MAX to find your line return, which is more than enough.