I've been trying to input 2 char arrays from user.
I want to truncate the input characters if they are more than specified length.
This is what I have done so far.
int main(){
printf("Enter Password: ");
char password[9]= {0};
fgets(password, sizeof(password), stdin);
printf("Enter key file path: ");
char file_path[200];
fflush(stdin);
fgets(file_path, sizeof(file_path), stdin);
puts(file_path);
return 0;
}
I get this output:
If I enter more than 8 chars, it automatically assigns charcaters above 8 to my file_path. It does not ask for the 2nd input!
PS: I tried scanf("%8s", password) instead of fgets. Same issue.
Please Help, Thanks
In OP's code, the input that does not fit in the first fgets() remains for subsequent input. Better code would consume the entire line and detect if the line is excessively long.
Use fgets() with a long enough buffer to look for incomplete line input.
Read at least 2 more characters: extra character and '\n'.
Perhaps use your own my_gets() to read a line.
// Read a line
// If input, without the \n fits in the destination, return `s`
// else return NULL
// Conditions: line != NULL, 0 < sz <= INT_MAX
char *my_gets(char *line, size_t sz) {
if (fgets(line, (int) sz, stdin) == NULL) {
line[0] = '\0';
return NULL; // EOF
}
size_t length = strlen(line);
if (length > 0 && line[length - 1] == '\n') {
line[--length] = '\0'; // Chop off \n
} else if (length == sz - 1) {
// Consume rest of line
bool looped = false;
int ch;
while ((ch = fgetc(stdin)) != '\n' && ch != EOF) {
looped = true;
}
if (looped) {
return NULL; // Line too long
}
}
return line;
}
Application
int main(void) {
printf("Enter Password: ");
char password[9];
if (my_gets(password, sizeof password) == NULL) {
return EXIT_FAILURE;
}
puts(password);
printf("Enter key file path: ");
char file_path[200];
if (my_gets(file_path, sizeof file_path) == NULL) {
return EXIT_FAILURE;
}
puts(file_path);
return EXIT_SUCCESS;
}
From a security standpoint, good to scrub password[] and line[] after code is done with it.
memset(password, 0, sizeof password);
Yet the call to fgets(), fgetc() are themselves not so secure as they are not specified to "cover their tracks" as they return. This is a deeper subject beyond this post.
Related
I have input like the following:
Someting sth
example
5 15
3
I want to scanf input by lines to get whole content of the line. But when reaching first digit (there can be spaces/tabs before it) I want to scanf it as int.
That's what I have come up with but it does not work as expected - cursor still does not stop at digit character.
char person_name[1000];
int n;
while (scanf("%[^\n/D]%*c", person_name) > 0) {
if (checkIfContainsNumber(person_name) == 0) {
appendToLinkedList(&head_ref, person_name);
} else {
break;
}
}
while (scanf("%d", &n) > 0) {
printf("%d ", n);
}
As far as I understand the problem, each line could be considered either as
a sequence of names or a sequence of integers.
So I would try to read the file line by line and analyse each extracted line
as one sequence or another (spaces are implicitly consumed).
The trick here is the usage of "%n" to go further in the analyse of the same line.
#include <stdio.h>
int
main(void)
{
FILE *input=fopen("input.txt", "r");
if(!input)
{
return 1;
}
char line[1024];
while(fgets(line, sizeof(line), input))
{
int pos=0;
int value, count;
char name[256];
if(sscanf(line+pos, "%d%n", &value, &count)==1)
{
pos+=count;
printf("a line with values: <%d>", value);
while(sscanf(line+pos, "%d%n", &value, &count)==1)
{
pos+=count;
printf(" <%d>", value);
}
printf("\n");
}
else if(sscanf(line+pos, "%255s%n", name, &count)==1)
{
pos+=count;
printf("a line with names: <%s>", name);
while(sscanf(line+pos, "%255s%n", name, &count)==1)
{
pos+=count;
printf(" <%s>", name);
}
printf("\n");
}
}
fclose(input);
return 0;
}
Read the input line-wise with fgets and keep a mode: TEXT for text, NUMBER for numbers and ERROR for an error condition. (The error condition is undescribed. It could occur when you encounter non-numeric data in NUMBER mode, for example.)
Start out with TEXT. Before processing a line in text mode, check whether it could be a digit by a simple sscanf into the line. If you can read a number, switch to number mode, where you scan all numbers from a line.
char line[80];
enum {TEXT, NUMBER, ERROR = -1} mode = TEXT;
while (mode != ERROR && fgets(line, sizeof(line), stdin)) {
if (mode == TEXT) {
int n;
if (sscanf(line, "%d", &n) > 0) mode = NUMBER;
}
if (mode == TEXT) {
line[strcspn(line, "\n")] = '\0';
process_string(line);
} else if (mode == NUMBER) {
char *p = line;
char *end;
int n = strtol(p, &end, 0);
if (end == p) mode = ERROR;
while (end > p) {
process_number(n);
p = end;
n = strtol(p, &end, 0);
}
}
}
(But this approach will fail if the numbers are all in one very long. fgets truncates the input so that the specified size will nor be exceeded.)
Consider changing the scan strategy - ignore all characters that are non-digit, and then read the integer from that digits forward
if ( scanf("%*[^0-9]%d", &n) == 1 ) { ... }
The first field '%*[...]' will skip over anything that is non-digit. Note that it's possible to reach EOF before finding a digit - if statement is needed to check.
I want to ask user to input some string in each loop. However, the code below will skip loop multiple times if the length of input is greater than 2?
Why is this happening and what's the best way to read arbitrary length input from stdin?
#include <stdio.h>
int main(void)
{
char in[2];
while (in[0] != 'q') {
puts("Enter: ");
fgets(in, 3, stdin);
}
return 0;
}
First, your buffer is not large enough to hold a 2 character string. Strings in C need to have an extra '\0' appended to them to mark the end. Second, fgets will leave chars in the input buffer when the input string is longer than the buffer passed to it. You need to consume those characters before calling fgets again.
Here is a function, similar to fgets in that it will only read buffer_len - 1 chars. And it will also consume all chars until the newline or EOF.
int my_gets(char *buffer, size_t buffer_len)
{
// Clear buffer to ensure the string will be null terminated
memset(buffer, 0, buffer_len);
int c;
int bytes_read = 0;
// Read one char at a time until EOF or newline
while (EOF != (c = fgetc(stdin)) && '\n' != c) {
// Only add to buffer if within size limit
if (bytes_read < buffer_len - 1) {
buffer[bytes_read++] = (char)c;
}
}
return bytes_read;
}
int main(void)
{
char in[3]; // Large enough for a 2 char string
// Re-arranged to do/while
do {
puts("Enter: ");
my_gets(in, sizeof(in)); // sizeof(in) == 3
} while (in[0] != 'q');
return 0;
}
Scenario:
I am trying to validate some user-input.
In my case the user is only allowed to
enter between 0 and 3 lowercase characters,
optionally including whitespace.
In Regex: ^[a-z ]{0,3}$
If the user enters more than 3 characters or
the input string contains invalid values,
in each case a different return-value and
error message has to be printed.
What I tried is to read the input into a temporary
char array and defining the scanset as 4[a-z ],
so that only the correct characters will be read
and one char more, in order to check if the maximal
number of desired characters has been read.
I.e. if the last element in this temporary array
is not empty the user input was bigger than 3.
Problem:
When the user enters 3 correct chars
and a 4th wrong char, the 4th won't be read,
therefore we read 3 valid chars,
we "allegedly" never read an invalid char and
the length of read chars is also valid,
all tough it of course is not!
Code:
//--------------------------------------
int scan_input(char* char_array)
{
int status = 0;
int max_size = 3;
char temp_array[max_size+1];
// Print system prompt:
printf("plain text: ");
// Read user input:
status = scanf("%4[a-z ]", temp_array);
if (temp_array[max_size] != '\0')
{
printf("[ERR] too many characters\n");
return -1;
}
if (status != 1)
{
printf("[ERR] invalid characters\n");
return -2;
}
strcpy(char_array,temp_array);
printf("[OK] Input is valid!\n");
return 0;
}
Output:
$ gcc -Wall -std=c11 application.c && ./a.out
plain text: abcD
[OK] Input is valid!
I am grateful for every hint to fix this blind spot!
PS.:
If you know a better approach to solve this problem, than by doing it with scanf() and the scanset, your thoughts are welcome!
to validate some user-input
Separate the input from the parsing
Use a wide buffer and fgets().
char buf[80];
if (fgets(buf, sizeof buf, stdin)) {
// we have some input
Then parse and use "%n", which records the scan position, to test success.
int max_size = 3;
char temp_array[max_size+1];
int n = 0;
temp_array[0] = '\0';
sscanf(buf, "%3[a-z ]%n", temp_array, &n);
bool success = buf[n] == '\n' || buf[n] == '\0';
If sscanf() did not scan anything, n == 0 and the prior temp_array[0] = 0 insures a null character.
If the scan succeeded, n > 0 and code inspects the next character.
Alternative staying with scanf()
status = scanf("%3[a-z ]", temp_array);
// When nothing read, form "" string
if (status != 1) {
temp_array[0] = '\0';
}
bool success = true;
if (status == EOF) {
success = false;
} else {
// consume rest of line, noting if extra junk followed
int next_ch;
while ((next_ch = fgetc(stdin)) != '\n' && next_ch != EOF) {
success = false; //Extra junk
}
}
Hi i am new to C and i am trying to use the Character array type below to captures input from users. How do i prevent or escape numerical characters. I just want only strings to be captured.
char str_input[105];
In have tried
scanf("%[^\n]s",str_input);
scanf("%[^\n]",str_input);
scanf("%[^0-9]",str_input);
scanf("%[A-Zaz-z]",str_input);
str_input = fgetc(stdin);
None of the above worked for me.
Input
2
hacker
Expected Output
Hce akr
int main() {
char *str_input;
size_t bufsize = 108;
size_t characters;
str_input = (char *)malloc(bufsize * sizeof(char));
if (str_input == NULL)
{
perror("Unable to allocate buffer");
exit(1);
}
characters = getline(&str_input,&bufsize,stdin);
printf("%zu characters were read.\n",characters);
int i;
int len = 0;
for (i = 0, len = strlen(str_input); i<=len; i++) {
i%2==0? printf("%c",str_input[i]): 'b';
}
printf(" ");
for (i = 0, len = strlen(str_input); i<=len; i++) {
i%2!=0? printf("%c",str_input[i]): 'b';
}
return 0;
}
Error
solution.c: In function ‘main’:
solution.c:21:5: warning: implicit declaration of function ‘getline’ [-Wimplicit-function-declaration]
characters = getline(&str_input,&bufsize,stdin);
Since your buffer has limited size, then using fgets(3) is fine. fgets() returns NULL on failure to read a line, and appends a newline character at the end of the buffer.
In terms of preventing numerical characters from being in your buffer, you can simply create another buffer, and only add non-numerical characters to it. You could just delete the numerical characters from your original buffer, but this can be a tedious procedure if you are still grasping the basics of C. Another method would be just to read single character input with getchar(3), which would allow you assess each character and simply ignore numbers. THis method is by far the easiest to implement.
Since you asked for an example of using fgets(), here is some example code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define INPUTSIZE 108
int main(void) {
char str_input[INPUTSIZE], characters[INPUTSIZE];
size_t slen, char_count = 0;
printf("Enter input:\n");
if (fgets(str_input, INPUTSIZE, stdin) != NULL) {
/* removing newline from fgets() */
slen = strlen(str_input);
if (slen > 0 && str_input[slen-1] == '\n') {
str_input[slen-1] = '\0';
} else {
fprintf(stderr, "Number of characters entered exceeds buffer size\n");
exit(EXIT_FAILURE);
}
/* checking if string is valid */
if (*str_input == '\0') {
fprintf(stderr, "No input found\n");
exit(EXIT_FAILURE);
}
printf("Buffer: %s\n", str_input);
/* only adding non-numbers */
for (size_t i = 0; str_input[i] != '\0'; i++) {
if (!isdigit(str_input[i])) {
characters[char_count++] = str_input[i];
}
}
/* terminating buffer */
characters[char_count] = '\0';
printf("New buffer without numbers: %s\n", characters);
}
return 0;
}
Example input:
Enter input:
2ttt4y24t4t3t2g
Output:
Buffer: 2ttt4y24t4t3t2g
New buffer without numbers: tttytttg
Update:
You could just use this even simpler approach of ignoring non-number characters:
char str_input[INPUTSIZE];
int ch;
size_t char_count = 0;
while ((ch = getchar()) != EOF && ch != '\n') {
if (!isdigit(ch)) {
if (char_count < sizeof(str_input)) {
str_input[char_count++] = ch;
}
}
}
str_input[char_count] = '\0';
If you're using Linux, I would use the getline() function to get a whole line of text, then verify it. If it is not valid input, I would in a loop ask the user to enter a line of text again and again until you the input is acceptable.
If not using Linux, well, your best bet is probably to reimplement getline(). You can also use fgets() if you find a limited-size buffer acceptable. I don't find limited-size buffers acceptable, so that's why I prefer getline().
getline() is used according to the way explained in its man page: http://man7.org/linux/man-pages/man3/getdelim.3.html
Basically, your loop should be something similar to:
char *buf = NULL;
size_t bufsiz = 0;
while (1)
{
if (getline(&buf, &bufsiz, stdin) < 0)
{
handle_error();
}
if (is_valid(buf))
{
break;
}
printf("Error, please re-enter input\n");
}
use_buffer(buf);
free(buf);
Well that's not possible. Numbers are string too. But you can set loop to look for numbers and print error. like this :
char *str = "ab234cid20kd", *p = str;
while (*p) { // While there are more characters to process...
if (isdigit(*p)) { // Upon finding a digit, ...
printf("Numbers are forbidden");
return 0;
} else {
p++;
}
}
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int individualAverage(int data[][20],int j)
{
int k,average=0;
for(k=0;k<10;k++)
{
average += data[k][j];
}
return average;
}
int main()
{
int var,indAvg=0;
int i=0,j,k;
char *experiments[20];
int data[10][20];
char str[100],str2[100];
char *ptr, *token;
int no_line=1;
while(fgets(str,100,stdin) != NULL && (strcmp(str,"*** END ***") && strcmp(str,"*** END ***\n")))
{
if(no_line % 2 == 0)
{
k=0;
token = strtok (str," ");
while (token != NULL)
{
sscanf (token, "%d", &var);
data[k++][i] = var;
token = strtok (NULL," ");
}
i++;
}
else
{
ptr = strdup(str);
experiments[i] = ptr;
}
no_line++;
}
fgets(str,100,stdin);
token = strtok(str," ");
while(token != NULL && (strcmp(token,"4") && strcmp(token,"4")))
{
sscanf (token, "%d", &var);
printf("DATA SET ANALYSIS\n1.\tShow all the data\n2.\tCalculate the average for an experiment\n3.\tCalculate the average across all experiments\n4.\tQuit\nSelection: %d\n\n",var);
switch(var)
{
case 1 :
for(j=0;j<i;j++)
{
printf("%s",experiments[j]);
for(k=0;k<10;k++)
{
printf("%d ",data[k][j]);
}
printf("\n");
}
printf("\n");
break;
case 2 :
printf("What experiment would you like to use?\n");
token = strtok (NULL," ");
sscanf (token, "%s", &str);
for(j=0;j<i;j++)
{
if(strcmp(experiments[j],str) == 0)
{
indAvg = individualAverage(data,j);
printf("Experiment: %s",experiments[j]);
printf("The individual average of the experiment is %d\n",indAvg);
break;
}
}
}
token = strtok(NULL," ");
}
}
OK, so I have a method that takes lines of redirection input. The lines come in pairs. First line is the name of an experiment, and the second line has the 10 values separated by spaces for that experiment. After these pairs, there is an ending line "*** END ***"
After this line, there is one last line holding the instructions of what to do with the data.
I'm currently having a problem where I've used fgets() to store the strings of the first pairs of lines into a variable which I declared as char *experiments[20];
Each of strings that this array is pointing to will have '\n' at the end of the string because of fgets()
Back to the last line of instructions. You have values 1-4. Right now I'm looking at instruction 2. It tells the average of an experiment. So after 2 on the last line, there must be the name of one of the experiments. I've used:
char str[100];
int var;
char *token;
token = strtok(str, " ");
sscanf (token, "%d", &var);
to get the first value on the line into var (pretend it's 2). So after that would be a string. Say it's Test 1, I'll use
token = strtok (NULL," ");
sscanf (token, "%s", &str);
to get the value into str, and then I'll compare it to experiments for all possible indexes.
HOWEVER, because fgets() gives '\n' at the end of the lines, all of the experiments strings will have '\n' at the end while str will just have the name of the experiment WITHOUT '\n' therefore they will never be equal even if '\n' is the only difference between the strings.
Any solutions?
Since you know that there may be a \n at the end of the string, you could check for it, and remove it if it's there:
size_t len = strlen(str);
if (len != 0 && str[len-1] == '\n') {
str[len-1] = '\0';
}
This would terminate the line at \n, so your strcmp would succeed. An alternative is to use strncmp, and pass the length of the target string. This runs the risk of false positives when there's a longer suffix that \n, though.
You could also read your data like this:
fscanf(f, "%99[^\n]", str);
You can make your own version of fgets that doesn't store the new-line character when it encounters one, and call it myfgets. Something like this would replicate fgets's behaviour, I think, produced with respect to the description given in MSDN:
char * myfgets( char * str, int n, FILE * stream ) {
if ( n <= 0 ) return NULL; // won't accept less than or equal to zero length
int currentPos = 0;
while ( n-- > 0 ) {
int currentChar = fgetc( stream );
if ( currentChar == EOF ) return NULL;
if ( currentChar == '\n' ) break;
// if these two lines were in reversed order,
// it would behave same as the original fgets
str[currentPos++] = currentChar;
}
return str;
}
But of course the other solution is simpler, hehe...