c fgets duplicates last character - c

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;
}

Related

resolving memory loss between two arrays in C

Good evening. I'm working on a program for class and I am hitting a brick wall when it comes to dealing with arrays using C.
--EDIT-- Full code has been posted.
#define _CRT_SECURE_NO_WARNINGS
#define STRMAX 20
#define MAX 100
#include<stdio.h>
int main()
{
int count = 0;
char strlist[STRMAX][MAX];
int start = 0, end = STRMAX;
for (start; start < end; start++) {
char string[MAX];
printf("Enter a string: ");
fgets(string, MAX - 1, stdin);
printf("\nThe string is: %s", string);
int size = strlen(string);
int result = strcmp(string, "stop\n");
if (result == 0) {
break;
}
strcpy(strlist[start], string);
count = count + 1;
}
char rev[STRMAX][MAX];
int temp = 0;
printf("count is: %d\n",count);
while (count != 0) {
strcpy(rev[temp], strlist[count]);
temp = temp + 1;
count = count - 1;
}
printf(rev);
return 0;
}
The last line, printf(rev); is throwing the warning: "using uninitialized memory 'rev'. "
I do not understand C, its the beginning of this course. However I am NOT looking for a "do my homework for me" answer, more of a "here is a better way to go about this" answer.
the output for the code is:
Enter a string: 1
The string is: 1
Enter a string: 2
The string is: 2
Enter a string: 3
The string is: 3
Enter a string: stop
The string is: stop
count is: 3
╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠3
the "count is: 3" is entirely for debugging. I really don't have a clue why my solution doesn't work. If there is any more information that is needed or anything else you would like to see feel free to ask and i'll update the post! thanks.
--EDIT--
STRMAX and MAX are both definitions set for the 2D array required for keeping an array of strings (20 and 50 respectively)
First of all, the line
strcpy(rev[temp], strlist[count]);
is wrong. Valid indexes for strlist are 0 to count-1, assuming that you only want to read valid strings. However, you are using the indexes 1 to count instead. Therefore, you should move the line
count = count - 1;
before that line.
Also, the line
printf(rev);
does not make sense.
If you want to print all strings in the array, then you should print every string individually, in a loop.
Since you are storing the number of valid strings in the variable temp, you must print that many strings.
for ( int i = 0; i < temp; i++ )
{
printf( "%s\n", rev[i] );
}
Also, you should #include <string.h>, because you are using strcpy and strlen.
Additionally, you probably should remove the trailing newline character from the input obtained from fgets. Otherwise, you will be printing that newline character, which will give you unwanted extra lines, forcing you to compensate by printing less newline characters explicitly. The existance of the newline character is also forcing you to add a newline character to the target string "stop":
int result = strcmp(string, "stop\n");
You will be able to remove that newline character from the target string if you also remove it from the input string.
After making these changes, your code should look like this:
#define _CRT_SECURE_NO_WARNINGS
#define STRMAX 20
#define MAX 100
#include <stdio.h>
#include <string.h>
int main()
{
int count = 0;
char strlist[STRMAX][MAX];
int start = 0, end = STRMAX;
for (; start < end; start++) {
char string[MAX];
printf("Enter a string: ");
fgets(string, MAX - 1, stdin);
//remove trailing newline character
string[strcspn(string,"\n")] = '\0';
printf("The string is: %s\n", string);
int result = strcmp(string, "stop");
if (result == 0) {
break;
}
strcpy(strlist[start], string);
count = count + 1;
}
char rev[STRMAX][MAX];
int temp = 0;
printf("count is: %d\n",count);
while (count != 0) {
count = count - 1;
strcpy(rev[temp], strlist[count]);
temp = temp + 1;
}
for ( int i = 0; i < temp; i++ )
{
printf( "%s\n", rev[i] );
}
return 0;
}
This program has the following output:
Enter a string: 1
The string is: 1
Enter a string: 2
The string is: 2
Enter a string: 3
The string is: 3
Enter a string: stop
The string is: stop
count is: 3
3
2
1

Copying valid strings to 2d array in C

I am checking if a function returns true, it prints out valid strings according some other function I got. At the moment, it's printing it out correctly but it is also printing empty lines which seem to correspond to the invalid strings.
How can I make these empty lines go away?
Here is my code:
int main()
{
int i, count = 0;
char input[10];
char validStr[10][60] = {""};
for (i = 0; i < 60; ++i){
if(fgets(input,10, stdin) == NULL){
break;
}
input[strcspn(input,"\n")] = '\0';
if(checkIfValid(input)){
memcpy(validStr[i],input,sizeof(input));
count++;
}
}
printf("%d\n",count);
for (int j = 0 ; j < count; ++j){
printf("%s\n",validStr[j]);
}
}
The count indicates it is printing only the valid strings but as you can tell by the pic it prints white lines.
Note: For various reasons the program needs to follow the current order so the output is printed after the first for loop.
Thanks in advance!
Instead of this:
if(checkIfValid(input)){
memcpy(validStr[i],input,sizeof(input));
count++;
}
This:
if(checkIfValid(input)){
memcpy(validStr[count],input,sizeof(input));
count++;
}
As others have pointed out in the comments, you want to safely secure that string copy. May I suggest:
if(checkIfValid(input)){
char* dst = validStr[count];
size_t MAXLEN = 10;
strncpy(dst, input, MAXLEN);
dest[MAXLEN-1] = '\0';
count++;
}
Continuing from the comment, if you want to store the entire string, you need to provide adequate space for the nul-terminating character.
AAAAAAAAAA
QELETIURTE
...
contain strings that are 10 characters long and will not fit in input as declared char[10].
Instead of looping with a for, allow the return from fgets() control your read-loop and keep count as a condition controlling the loop to ensure you protect your array bounds, e.g.
#include <stdio.h>
#include <string.h>
#define MAXC 128 /* if you need a constant, #define one (or more) */
#define NSTR 10
int checkIfValid (const char *s) { return 1; (void)s; }
int main(void)
{
size_t count = 0;
char input[MAXC];
char validStr[NSTR][MAXC] = {""};
while (count < NSTR && fgets (input, sizeof input, stdin)) {
input[strcspn(input,"\n")] = '\0';
if(checkIfValid(input)){
strcpy (validStr[count], input);
count++;
}
}
printf ("%zu\n",count);
for (size_t j = 0 ; j < count; ++j) {
printf("%s\n",validStr[j]);
}
}
(adjust your array declaration for 60 strings of 10 characters each)
If you want to cut off at 9 characters and ensure the stings are nul-terminated, #selbie has that covered.
Example Use/Output
With your data (as good as I could read it) in dat/validstr.txt you could do:
$ ./bin/validstring <dat/validstr.txt
6
AAAAAAAAAA
QELETIURTE
321qweve
sdsdsdfFF
GRSGGFDDSS
toLotssAAA

Segmentation Fault while reading multiple lines from a text file character-by-character

I am trying to read lines from a file, one character at a time.
Input example:
5 2
2
1 3
2 4 5
3
Suppose I am trying to read the first line, then I am reading '5' first, then the space (' ') and then the '2', and then trying to store the two number in a array, which I am declarig using realloc because I don't know how many numbers will be there in a line before reading the line.
I only knwo the number of lines that will be present in the file numNodes.
I am trying to store the numbers in a 2D array, where each row will have differnet lengths.
Since I don't know th length of each line beforehand, I am allocating memory using realloc after reading each number.
Since the numbers can be more than one-digited, i am storing the characters in num character array and when i encounter a space(' ') or newline('\n'), I convert it to a number using atoi, and store it in the suitable row, after allocating memory to it using realloc.
here's my code
FILE *fp;
fp = fopen("input.txt","r");
char ch;
int numNodes = 5, i = 0;
int **adjList = (int **)malloc(numNodes*sizeof(int *));
int len = 0,j,k=0;
char num[4];
for(i=0;i<numNodes;i++)
{
adjList[i] = NULL;
printf("Node %d -> ",i+1);
do
{
//read =
fscanf(fp,"%c",&ch);
if(ch!=' ' && ch!='\n')
{
num[k]=ch;
k++;
}
else
{
k = 0;
len++;
adjList[i] = (int *)realloc(adjList[i],sizeof(int)*len);
adjList[i][len-1] = atoi(num);
for(k=0;k<4;k++)
num[k]='0';
}
}
while(ch!='\n');
for(j=0;j<len;j++)
printf("%d ",adjList[i][j]);
printf("\n");
len = 0;
}
However, I am getting Segmentation Fault (core dumped) on the last line, that is, after 2 4 5 gets printed.
Any help is appreciated, as to, why this error is occuring on the last line?
EDIT :
Changed ch to &ch in fscanf line
Changed ch[0] to ch(if statement)
Added adjlist[i] = NULL before do...while loop
fscanf(fp,"%c",&ch);
if(ch!=' ' && ch!='\n')
{
num[k]=ch;
k++;
}
else
{
k = 0;
len++;
adjList[i] = (int *)realloc(adjList[i],sizeof(int)*len);
adjList[i][len-1] = atoi(num);
for(k=0;k<4;k++)
num[k]='0';
}
When you finish reading one number k will be 4.
for(k=0;k<4;k++)
num[k]='0';
And you start reading the next number with k >= 4 thus access out of bound for num .
num[k]=ch;
k++;
Also you need to terminate the num with \0 char to be used with atoi.
num[k] = '\0';
adjList[i][len-1] = atoi(num);

Last element is missing from the 2D array

Code:
#include <stdio.h>
int main(){
int num;
scanf("%d", &num);
printf("Enter: ");
char nums[5][num], ch;
for(int i = 0; i < num; i++){
for(int j = 0; j < 5; j++){
if((ch = getchar()) != '\n'){
nums[j][i] = ch;
}
}
}
for(int i = 0; i < num; i++){
for(int j = 0; j < 5; j++){
printf("%c ", nums[j][i]);
}
printf("\n");
}
return 0;
}
Output:
1
Enter: 12345
1 2 3 4
Process returned 0 (0x0) execution time : 6.282 s
Press ENTER to continue.
Why the last element is missing and the additional space at the beginning of the output of array?
If I change the range for j in both for loops to
j <= 5
then, the output looks like this:
1
Enter: 12345
1 2 3 4 5
Process returned 0 (0x0) execution time : 2.107 s
Press ENTER to continue.
If the initial value for j is 1 in the printf loop, then the output looks like this:
1
Enter: 12345
1 2 3 4 5
Process returned 0 (0x0) execution time : 3.675 s
Press ENTER to continue.
No extra gap at the beginning of the array output.
Can anyone explain this and how to resolve this problem?
The problem is that the function getchar reads all characters from the input stream including the new line character that stored in the buffer after the first call of scanf.
So in the loops the first character that is read is the new line character '\n'.
You should remove it for example the following way.
scanf( "%*[^\n]" );
scanf( "%*c" );
You have a logic issue in the first loop. You test for \n but then if you do find a \n, you leave the array entry uninitialized and go on to the next entry anyway. This leads to the bogus output.
Instead you could delay the j++ until getting a valid character, e.g. :
for(int i = 0; i < num; i++){
for(int j = 0; j < 5; ){
if((ch = getchar()) != '\n'){
nums[j][i] = ch;
++j;
}
}
}
Then the array will be filled with the non-newline characters that get entered, and you don't need to do any other flushling.
It would improve the code to also check ch != EOF (and ch should be declared as int), but then you will need some error handling (it would be a mistake to just break the loop and go on to try and output the whole array).

Check if stdin pipe is empty in C

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;
}

Resources