I am using user input to fill a 2d array. A user inputs numbers in one line and I then use nested loops to fill the array like so:
//User inputs: "1 2 3 4 5"
for(i = 0; i < r; i++){
for(j = 0; j < c; j++){
scanf("%d", &arr[i][j]);
}
}
However, the problem is if the user enters 5 ints when there is room for 6, it just waits for another input. How can I detect if there are insufficient numbers?
I have tried using this but it didn't work:
for(i = 0; i < r; i++){
for(j = 0; j < c; j++){
if (!feof(stdin)){
scanf("%d", &arr[i][j]);
}
else{
printf("insufficient datapoints\n");
}
}
}
One way to accomplish your goal would involve using fgets() instead of scanf() to read in a line of input at a time. Then strtok() can be used to break the line of input into tokens, and strtol() can be used to parse the tokens into numbers. Compared with scanf(), it is much easier to use fgets to handle unstructured user input.
The code below does this. If there are too many elements on an input row, too few elements, or if one of the elements is not a valid number, a message is printed and the row must be entered again.
As each line is entered by the user, strtok() is used to break the line into tokens. The list of token delimiters is stored in delims[]. Note that tokens may be separated by spaces or tabs; the delimiters themselves are not part of the token, so including \r and \n ensures that these characters will not be part of the final token in a line.
When a token is found, strtol() is used to convert it to an integer, if possible. After the call to strtol(), the pointer tail points to the first character in the token that was not part of a number; if tail points to the NUL terminator, then the entire string was parsed as a number, otherwise the input is considered bad and the row must be entered again.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUF_SIZE 1000
int main(void)
{
size_t r = 3;
size_t c = 5;
size_t i, j;
char buffer[BUF_SIZE];
char *token;
char *tail;
const char delims[] = " \t\r\n";
int arr[r][c];
int temp_val;
printf("Enter rows of %zu data elements:\n", c);
for(i = 0; i < r; i++){
j = 0;
if (fgets(buffer, BUF_SIZE, stdin) == NULL) {
perror("Error in fgets()");
exit(EXIT_FAILURE);
}
token = strtok(buffer, delims);
while (token != NULL) {
temp_val = strtol(token, &tail, 10);
if (*tail == '\0') {
arr[i][j] = temp_val;
++j;
} else { // token not a valid number
j = 0;
break;
}
if (j > c) { // too many input values
break;
}
token = strtok(NULL, delims);
}
if (j != c) {
printf("insufficient datapoints\n");
--i; // enter row again
}
}
for (i = 0; i < r; i++) {
for (j = 0; j < c; j++) {
printf("%5d", arr[i][j]);
}
putchar('\n');
}
return 0;
}
Sample interaction:
Enter rows of 5 data elements:
1 2 3 4
insufficient datapoints
1 2 3 4 5 6
insufficient datapoints
1 x 2 3 4
insufficient datapoints
1 2 3 4 x
insufficient datapoints
1 2 3 4 5 x
insufficient datapoints
1 2x 3 4 5
insufficient datapoints
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
You can use peek ahead in the stream and test characters before you actually consume them. (Well sort of in c).
You can use this to ignore whitespace (you will need to do this).
You can also use this peeked value to indicate if insufficient characters have been input.
The peek needs to be done BEFORE the actual read (scanf).
Added rough example code below
#include <stdio.h>
#include <ctype.h>
int r=3;
int c=2;
int arr[100][100]; // FIX magic
int main(int argc, char** argv[]) {
for(int i=0; i<r; i++) {
for(int j=0; j<c; j++) {
if (feof(stdin)) {
// error check and error / normal exit etc.
printf("eof\n");
}
char c=getchar();
if (c=='\n') {
// error check and error / normal exit here
printf("newline\n");
} else if (isspace(c)) {
// advance and remove them - watch for end of stream when winding
printf("advance and discard whiitespace\n");
} else { // add check for isdigit
// push back
ungetc(c, stdin);
printf("ungetc\n");
}
scanf("%d", &arr[i][j]);
printf("got %d\n", arr[i][j]);
}
}
return 0;
}
Related
I am trying to read integer values from stdin. I have a inner while loop that detects integer series so that I can parse ints with multiple characters. I strtok the buffer with newline delimiters because input can have integers over multiple lines.
My code to handle this is:
while (fgets(buf, BUF_SIZE, stdin)) {
strtok(buf, "\n");
for(int i = 0; buf[i] != '\0'; i++) {
size_t j = 0;
if(isdigit(buf[i])) {
while(isdigit(buf[i+(int)j])) {
j++;
}
char *new_str = malloc(j*sizeof(char));
size_t k =0;
while(k < j) {
new_str[k] = buf[i+(int)k];
k++;
}
printf("%s\n", new_str);
free(new_str);
}
}
}
The input could be:
1 9 10 10 11
The output should be:
1
9
10
10
11
The output I get is:
1
9
10
0
10
0
11
1
So every last character of input with n>1 gets read twice by the buffer some way.
I am unsure how this is possible but can't figure it out.
This happens because you grow j over the input string but you forget to grow i together with j. So you grow j and after you print it, you will grow i by 1 from the last value, and that i+1 will fall inside the input string that was already printed...
The solution is to reinitialize i so:
if(isdigit(buf[i])) {
.....
free(new_str);
i = i+j;
}
I wrote a program that scans an unknown amount of integers into an array but when I run it, it print the last value it has gotten an infinite amount of times.
For example for the input: 1 2 3 4 5
The output would be 55555555555555555555555...
Why does this happen and how can I fix that?
My goal here is to create a array, for an instance {1, 2, 3, 4, 5} and then print what it scanned into the array, ONLY ONCE...
int *pSet = (int*) malloc(sizeof(int)); int i; int c;
printf("Please enter a stream of numbers to make a set out of them: ");
printf("\n");
scanf("%d", &c);
pSet[0] = c;
printf("%d ", c);
for(i = 1; c != EOF; i++) {
pSet = (int*) realloc(pSet, sizeof(int)*(i+1));
if(pSet == NULL) {
return FAIL;
}
scanf("%d", &c);
pSet[i] = c;
printf("%d ", c);
}
free(pSet);
Why does this happen (?) (print ... an infinite amount of times.)
Look at the loop terminating conditions c != EOF.
int c;
scanf("%d", &c);
for(i = 1; c != EOF; i++) { // Not good code
scanf("%d", &c);
}
EOF is some negative value, often -1. scanf("%d", &c) attempts to read user input and convert to an int. scanf() returns a 1,0,EOF depending on if it 1) succeeded, 2) failed to find numeric text or 3) end-of-file or input error occurred. Unfortunately code does not use that return value. Instead code used the number read, c and checked if that number read was the same as EOF.
how can I fix that?
Only loop when the return value of scanf() is as expected (1).
for(i = 1; scanf("%d", &c) == 1; i++) {
...
}
Putting this together with some other ideas
#include <stdio.h>
#include <stdio.h>
int main(void) {
printf("Please enter a stream of numbers to make a set out of them:\n");
int *pSet = NULL; // Start with no allocation
size_t i = 0;
int c;
for (i = 0; scanf("%d", &c) == 1; i++) {
// +--------------------------- No cast needed.
// v v----------v Use sizeof de-referenced pointer
void *p = realloc(pSet, sizeof *pSet * (i + 1));
if (p == NULL) {
free(pSet);
return EXIT_FAILURE;
}
pSet = p;
pSet[i] = c;
}
for (size_t j = 0; j < i; j++) {
printf("%d ", pSet[j]);
}
free(pSet);
return 0;
}
There are a number of problems.
1) Terminate the loop when scanf fails instead of using EOF. Do that by checking that the return value is 1 (i.e. the number of input items
successfully matched)
2) Don't allocate memory until it's needed
3) Never do realloc directly into the target pointer - always use a temp variable.
Fixing this your code could be:
#include <stdio.h>
int main(void) {
int *pSet = NULL;
printf("Please enter a stream of numbers to make a set out of them: ");
printf("\n");
int i = 0;
int c;
while (1) {
if (scanf("%d", &c) != 1)
{
printf("Terminating input loop\n");
break;
}
int* tmp = realloc(pSet, sizeof(int)*(i+1));
if(tmp == NULL) {
printf("oh dear...\n");
break;
}
pSet = tmp;
pSet[i++] = c;
printf("%d ", c);
}
for (int j=0; j < i; ++j) printf("%d\n", pSet[j]);
free(pSet);
return 0;
}
Input:
1 2 3 4 5 6 7 stop
Output:
Please enter a stream of numbers to make a set out of them:
1 2 3 4 5 6 7
Terminating input loop
1
2
3
4
5
6
7
You should stop your loop when scanf fails. According to the manual:
On success, [scanf] return[s] the number of input items successfully matched and assigned; this can be fewer than provided for, or even zero, in the event of an early matching failure.
The value EOF is returned if the end of input is reached before either the first successful conversion or a matching failure occurs. EOF is also returned if a read error occurs. [...]
So you can turn your for loop into a while one.
#include <stdio.h>
#include <stdlib.h>
#define FAIL 0
int main() {
int *pSet = (int*) malloc(sizeof(int));
int c;
int i=0;
printf("Please enter a stream of numbers to make a set out of them: ");
while(scanf("%d", &c) == 1) {
pSet[i] = c;
pSetNew = (int*) realloc(pSet, sizeof(int)*(i+1));
if(pSetNew == NULL) {
free(pSet);
return FAIL;
} else {
pSet = pSetNew;
}
printf("%d ", c);
i++;
}
free(pSet);
}
But if you want a more robust piece of code, I suggest you to retrieve the answer as a string (NULL-terminated array of char), and then parse it with dedicated functions like strtol which let you check if the whole string is a valid entry, and not only the first characters.
Note: HengLi fixed a potential memory leak in the code sample above
Saying that the inputs(stdin) are:
6 4
0 1 1 1 1 0
1 0 0 0 0 1
1 0 0 0 0 1
0 1 1 1 1 0
In the first line, 6 and 4 are width and height respectively.
I did
fgets(buf, sizeof(buf), stdin);
if(strlen(buf) > 4 || strlen(buf) < 4)
{
printf("Cannot decode\n");
return 1;
}
So that if I put the number of integers that is greater than or less than 2 as for the first line(width and height), then press enter, it occurs error.
Next step is to put the rest of the inputs in 2D array, board[height][width].
What I did is:
for(i = 0; i < height; i++)
{
for(j = 0; j < width; j++)
{
scanf("%d", &input);
board[i][j] = input;
}
}
But the problem is, the matrix needs to match the width and height.
For example, if I enter 6 and 4 as its width and height respectively,
and then if I put
0 1 1 1 1
then press enter, it needs to occur an error immediately as the number of integers and the width do not match.
scanf ignores the enter key... so how can I make the program occur an error if I put a number of integers that do not match width and height?
so how can I make the program occur an error if I put a number of integers that do not match width and height?
One approach could be:
1) Read a whole line into a string
2) Try to read width integers from the string
Repeat that height times.
To track the progress in the string (step 2), you can use %n in the sscanf calls to find how many chars that each call has used.
Something like:
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char *argv[])
{
int i, j;
int height, width;
char buf[256];
// Read size
if (fgets(buf, sizeof(buf), stdin) == NULL)
{
printf("Illegal input\n");
exit(1);
}
if (sscanf(buf, "%d %d", &width, &height) != 2)
{
printf("Illegal input\n");
exit(1);
}
// Use VLA (note: only do this for small matrices)
int board[height][width];
// Read the matrix
for (i=0; i < height; ++i)
{
if (fgets(buf, sizeof(buf), stdin) == NULL)
{
printf("Illegal input\n");
exit(1);
}
int consumed = 0;
int consumed_current;
for (j=0; j < width; ++j)
{
// Scan from string offset "consumed"
if (sscanf(buf+consumed, "%d%n", &board[i][j], &consumed_current) != 1)
{
printf("Illegal input\n");
exit(1);
}
// Move the offset in the string
consumed = consumed + consumed_current;
}
}
// Print
for (i=0; i < height; ++i)
{
for (j=0; j < width; ++j)
{
printf("%d ", board[i][j]);
}
printf("\n");
}
return(0);
}
Notice: This code only checks for too few data elements. If an input line contains too many elements, this code will just ignore the rest. Checking for "too many" elements can be done by verifying that the whole input string has been consumed by sscanf.
I have a basic algorithm i want to try out, and to do so i have a txt file with a number of inputs. To do so i redirect the stdin to my input.txt using the command, in the compiler: ./prog < input.txt. Here's the mentioned code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void ExA(int n, int VALOR, int *sequencia){
int i,j,z,soma;
printf("%d - %d - %d\n",sequencia[0],sequencia[1],sequencia[2]);
for(i=1; i<=n; i++){
for(j=i; j<=n; j++){
soma = 0;
for(z=i; z<=j; z++){
soma = soma + sequencia[j];
}
if(soma == VALOR){
printf("SUBSEQUENCIA NA POSICAO %d\n", i);
return;
}
}
}
printf("SUBSEQUENCIA NAO ENCONTRADA\n");
}
int main(void){
int n, i, VALOR;
int *sequencia = malloc(sizeof(int));
char *buffer = malloc(sizeof(int) * 4);
while(read(0, buffer ,sizeof(buffer))){
sscanf(buffer,"%d %d",&n ,&VALOR);
if(n == 0 && VALOR == 0){
return 0;
}
else{
for(i = 0; i<n; i++){
read(0, buffer ,sizeof(buffer));
sscanf(buffer,"%d",&sequencia[i]);
}
}
ExA(n,VALOR,sequencia);
}
return 0;
}
The main function is responsible for reading from the input file and sending the values to the algorithm ExA. In my input.txt file i start with the following:
3 5
1
3
4
4 5
1
5
4
2
However when i print out my array (in the beginning of the ExA function) where the numbers should be stored it prints out 1 - 4 - 5 instead of the wanted 1 - 3 - 4
Note that read() does not deal with strings — it does not add a null terminator to what it reads. You are, therefore, passing sscanf() a byte array and not a guaranteed (null terminated) string.
Also, read() pays no attention whatsoever to line breaks. It reads 4 or 8 bytes at a time (unless you're on a very unusual machine these days) because sizeof(buffer) is the size of a pointer. Assuming you have a 64-bit machine, the first read() processes the first 3 lines (3 5, 1, 3 — they happen to be 8 bytes long); the second read() gets the second 3 lines (4, 4 5, 1) and reports appropriately.
You should capture and test the return value from read(). You should print what it gets. (Partially fixed code — more work to be done to get the program fully working.)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static void ExA(int n, int VALOR, int *sequencia)
{
int i, j, z, soma;
printf("%d - %d - %d\n", sequencia[0], sequencia[1], sequencia[2]);
for (i = 1; i <= n; i++)
{
for (j = i; j <= n; j++)
{
soma = 0;
for (z = i; z <= j; z++)
{
soma = soma + sequencia[j];
}
if (soma == VALOR)
{
printf("SUBSEQUENCIA NA POSICAO %d\n", i);
return;
}
}
}
printf("SUBSEQUENCIA NAO ENCONTRADA\n");
}
int main(void)
{
int n, i, VALOR;
int *sequencia = malloc(sizeof(int));
char *buffer = malloc(sizeof(int) * 4);
while (read(0, buffer, sizeof(buffer)))
{
sscanf(buffer, "%d %d", &n, &VALOR);
if (n == 0 && VALOR == 0)
{
return 0;
}
else
{
for (i = 0; i < n; i++)
{
int nbytes = read(0, buffer, sizeof(buffer));
printf("Read [%.*s]\n", nbytes, buffer);
buffer[nbytes] = '\0';
sscanf(buffer, "%d", &sequencia[i]);
}
}
ExA(n, VALOR, sequencia);
}
return 0;
}
Given the input shown in the question, I get the output:
Read [4
4 5
1
]
Read [5
4
2
]
[]
4 - 5 - 5
SUBSEQUENCIA NA POSICAO 1
This much work cleans up the code — and explains some of the odd result you were getting. It doesn't make full use of the space allocated (which is bigger than sizeof(buffer), so the buffer[nbytes] = '\0'; assignment is within bounds of the array. It reads the same value repeatedly in the sscanf() call because you don't tell it to read from different locations in the string. If you need to read multiple values from a buffer using scanf(), read the answers about How to use sscanf() in a loop?
You should consider using fgets() or maybe POSIX getline() to read lines. If you're sure the data is clean, you could even use scanf() directly, but it is usually better to read lines and then parse them with sscanf().
If the exercise requires you to read with the read() system call, then you will have to work out a scheme where you read a buffer full of data and dole the contents out in chunks.
I have this c program where I am inputing a number N followed by N more numbers. For example, I'll enter 100 followed by 100 more numbers. For some reason, after so many inputs the scanf function will stop working properly. It's as if it has stopped taking input and will just continue one with whatever value is in size.
The use case I came up with is 100 1 2 3 4 5 6 7 8 9 10... (repeated ten times). then after three or four times of that I'll type in 100 10 9 8 7 6 5 4 3 2 1... (repeated ten times) and then there will be an infinite loop of print statements.
int main(int argc, const char * argv[]) {
int histogram[10000];
int i;
while (1) {
int *rectPtr = histogram;
int size;
scanf("%d", &size);
if (!size) return 0;
for (i = 0; i < size; ++i) {
scanf("%d", rectPtr);
rectPtr++;
}
printf("%d", 1);
printf("\n");
}
return 0;
}
Distrust infinite loops.
In a series of comments, I said:
You're not testing the return value from scanf(), so you don't know whether it is working. The pair of printf() statements is odd; why not write printf("%d\n", 1); or even puts("1");?
Your code does not test or capture the return value from scanf(), so you do not know whether scanf() is reporting a problem. As a general rule, test the return value of input functions to make sure what you thought happened did in fact happen. You could also print out the values read just after you read them:
if (scanf("%d", rectPtr) != 1)
{
fprintf(stderr, "scanf() failed\n");
return 1;
}
printf("--> %d\n", *rectPtr);
rectPtr++;
Similarly when inputting size. Also consider if (size <= 0) return 0;. And using fgets() plus `sscanf() can make reporting errors easier.
j.will commented:
It is great to know if scanf fails, but I want to know why it fails and prevent it from failing. How do I do that?
I responded:
I understand you'd like to know. With scanf(), the best you can do after a failure is usually to read all the characters that follow up to a newline or EOF, and if you want to know what went wrong, then you print those characters too, because scanf() leaves the last character that it read in the input buffer ready for the next input operation.
void gobble(void)
{
printf("Error at: <<");
int c;
while ((c = getchar()) != EOF && c != '\n')
putchar(c);
puts(">>");
if (c == EOF)
puts("<<EOF>>");
}
The first character in the output is what caused the failure.
See also How to use sscanf() in loops?
Hacking your code to match this:
#include <stdio.h>
static void gobble(void)
{
printf("Error at: <<");
int c;
while ((c = getchar()) != EOF && c != '\n')
putchar(c);
puts(">>");
if (c == EOF)
puts("<<EOF>>");
}
int main(void)
{
enum { MAX_VALUES = 10000 };
int histogram[MAX_VALUES];
int size;
while (printf("Number of items: ") > 0 && scanf("%d", &size) == 1 &&
size > 0 && size <= MAX_VALUES)
{
int *rectPtr = histogram;
for (int i = 0; i < size; ++i)
{
if (scanf("%d", rectPtr) != 1)
{
gobble();
return 1;
}
rectPtr++;
}
printf("size %d items read\n", size);
}
return 0;
}
IMO, you need to check the return value of scanf() for proper operation. Please check the below code. I have added some modifications.
To exit from the program, you need to press CTRL+ D which will generate the EOF. Alternatively, upon entering some invalid input [like a char instead of int] wiil also cause the program to beak out of while() llop and terminate.
I have put the sequence to check first scanf(). All others need to be checked, too.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char * argv[]) {
int histogram[10000] = {0};
int i;
int *rectPtr = histogram;
int size = 0;
int retval = 0;
printf("Enter the number of elements \n");
while ( (retval = scanf("%d", &size)) != EOF && (retval == 1)) {
rectPtr = histogram;
if (!size) return 0;
printf("Enter %d elements\n", size);
for (i = 0; i < size; ++i) {
scanf("%d", rectPtr); //check in a simmilar way to above
rectPtr++;
}
printf("%d\n", 1111111);
printf("Enter the number of elements: \n");
}
return 0;
}
The output of a sample run
[sourav#broadsword temp]$ ./a.out
Enter the number of elements: 2
Enter 2 elements
1
2
1111111
Enter the number of elements: 3
Enter 3 elements
1
2
3
1111111
Enter the number of elements: 9
Enter 9 elements
0
9
8
7
6
5
4
3
2
1111111
Enter the number of elements: r
[sourav#broadsword temp]$
histogram is declared to have size 10000. You say you do 100 1 2 3 ... repeated 10 times. If I correctly understand that uses 1000 slots in histogram.
If you repeat the test more than 10 times, you exhaust histogram and begin to write past the end of array causing undefined behaviour.
So you must either :
reset recPtr = histogram at each iteration
control recPtr - histogram + size <= sizeof(histogram) after reading size (IMHO better)
And as other said, you should always control input operations : anything can happen outside of your program ...