Check if all values entered into char array are numerical - arrays

I have a char array, where should be only numbers (10 digits). If the user enters letters or special characters (even among those digits) the program should prompt the user again to enter the number.
I tried so many ways of doing it, but still couldn't find a way.
That's what I could do so far:
int f = 1;
int i = 0;
int flag =1;
char num[11];
printf("Enter a number: ");
while (f == 1) {
scanf("%10s", num);
while (flag == 1 && isdigit(num[i])) {
i++;
if (i == 10) {
f = 0;
flag =0;
}
}
if (!isdigit(num[i])) {
printf("Enter numerical char: ");
}
}
After I enter incorrect value it displays an empty line. If I put any value to that empty line, only then it says "Enter numerical char: " and prompts to enter the num again.
P.S. I know there is a way of ensuring that only numerical values entered, considering the fact that characters, unlike numbers are included in single quotes. (have no idea how to do this tbh)
Thanks in advance!

There are a couple of ways to do this.
The simple way is to use fgets to get a string.
Then, we can use strtol to decode the number [and we check the ending char for validity].
To do it completely manually, we can use isdigit in a loop, building up the number one digit at a time.
Here's some example code that is annotated and shows both ways:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <termios.h>
// getstr -- get a string with prompt
// RETURNS: length or (<0 -> error)
int
getstr(char *buf,int buflen,const char *prompt)
{
char *cp;
int ret = 0;
// decide if stdin is:
// (1) a TTY
// (2) a [redirected] file (e.g. invoked with ./myprogram < input)
static int echoflg = -1;
if (echoflg < 0) {
struct termios tio;
echoflg = (tcgetattr(fileno(stdin),&tio) < 0);
}
// NOTE: usage of the error codes in errno.h is arbitrary
while (ret <= 0) {
// ensure buffer has enough space
if (buflen < 2) {
ret = -ENOMEM;
break;
}
// output prompt
printf("%s: ",prompt);
fflush(stdout);
// get a line
cp = fgets(buf,buflen,stdin);
// EOF
if (cp == NULL) {
ret = -ENODATA;
break;
}
// echo file input to simulate TTY input
if (echoflg)
fputs(buf,stdout);
// get buffer length
ret = strlen(buf);
// empty string
if (ret <= 0)
continue;
// point to last char
cp = &buf[ret - 1];
// ensure we got a newline -- if not, fgets had to chop the line (i.e.)
// the line is too long to fit in the buffer
if (*cp != '\n') {
ret = -ENOSPC;
break;
}
// strip the newline -- we are done
*cp = 0;
--ret;
}
return ret;
}
// getnum_strtol -- get number using strtol
long
getnum_strtol(const char *prompt)
{
int len;
int readflg = 1;
char *cp;
char buf[100];
long num = 0;
while (readflg) {
len = getstr(buf,sizeof(buf),prompt);
if (len < 0)
exit(1);
num = strtol(buf,&cp,10);
// ensure we got a least one digit
if (cp <= buf)
continue;
switch (*cp) {
case ' ':
case '\t':
case 0:
readflg = 0;
break;
default:
printf("getnum_strtol: not a valid number -- buffer '%s', invalid '%s'\n",
buf,cp);
break;
}
}
return num;
}
// getnum_manual -- get number _not_ using strtol
long
getnum_manual(const char *prompt)
{
int len;
int readflg = 1;
int sign = 0;
int valid;
int chr;
char *cp;
char buf[100];
long num = 0;
while (readflg) {
len = getstr(buf,sizeof(buf),prompt);
// fatal error
if (len < 0)
exit(1);
// point to buffer start
cp = buf;
// find first non-whitespace character
valid = 0;
while (1) {
chr = *cp;
// end of string
if (chr == 0)
break;
// found character
valid = ((chr != ' ') && (chr != '\t'));
if (valid)
break;
++cp;
}
if (!valid)
continue;
// reset the accumlated number and the sign
num = 0;
sign = 0;
valid = 0;
// loop through all characters in buffer
while (1) {
chr = *cp++;
// get the sign of the number (and skip an explicit sign)
if (sign == 0) {
switch (chr) {
case '+':
sign = 1;
chr = *cp++;
break;
case '-':
sign = -1;
chr = *cp++;
break;
default:
sign = 1;
break;
}
}
// stop decoding number on whitespace
switch (chr) {
case ' ':
case '\t':
chr = 0;
break;
}
// check for clean end of number
if (chr == 0) {
if (valid) {
readflg = 0;
break;
}
}
// not a valid digit
if (!isdigit((unsigned char) chr)) {
cp -= 1;
printf("getnum_manual: not a valid number -- buffer '%s', invalid '%s'\n",
buf,cp);
break;
}
// add digit to number
num *= 10;
chr -= '0';
num += chr;
// we got at least one valid digit
valid = 1;
}
}
// apply sign
num *= sign;
return num;
}
int
main(int argc,char **argv)
{
char *cp;
int opt_s = 0;
long num;
// skip over program name
--argc;
++argv;
// get options
for (; argc > 0; --argc, ++argv) {
cp = *argv;
if (*cp != '-')
break;
cp += 2;
switch (cp[-1]) {
case 's': // use strtol
opt_s = !opt_s;
break;
}
}
while (1) {
if (opt_s)
num = getnum_strtol("Enter number [strtol]");
else
num = getnum_manual("Enter number [manual]");
printf("The number entered is: %ld\n",num);
if (num == 999)
break;
}
return 0;
}
Edit: Added code to replay/echo input from file.

Working with input from stdin in C can be a pain. I tried to explain what needs to be done in the comments while maintaining your variables in the manner in which you wanted them to be used. But I did need to add int ch to capture the input from stdin. The manner in which I captured input is FAR! from ideal but it illustrates the idea and does what you need it to do. But, to improve on your answer I'd suggest moving it into it's own function. Also, it ignores input of digits greater than 10 characters, so if for example you input 012345678901234567 it would just ignore the input beyond the 10th digit but still pass the test per your variables and flags. The comments should be self explanatory. I did not test this other than to make sure it compiled and once for digits and once for digits and text, but the idea is there, and you should be able to expand on it to match your requirements.
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h> /* for fflush */
int main()
{
int f = 1; /* set f on */
int i = 0;
int flag = 0; /* set flag OFF */
char num[11];
int ch; /* adding ch to read characters from stdin */
printf("Enter a number: "); /* flush stdout if no newline */
fflush(stdout);
while(f == 1) {
/* you need to read stdin to the newline */
/* but not exceed the bounds of num */
ch = fgetc(stdin);
while(ch != '\n' && ch != EOF) {
if(i < 10) /* reusing your i variable that you set to 0, don't exceed 10 to save space for '\0' */
num[i] = ch;
i++;
ch = fgetc(stdin); /* get another character */
}
/* now add '\0' to terminate your string */
/* but not beyond num[10] */
if(i > 10) {
num[10] = '\0';
} else {
num[i] = '\0';
}
/* now that we are done reading stdin reset i to 0 to restore how you had it */
i = 0;
/* lets test num working with your variables */
while(num[i] != '\0' && flag == 0) {
/* test for digit at num[i] */
if(!isdigit(num[i]))
flag = 1; /* not a digit so set your flag variable on */
i++;
}
/* process your flag variable */
if(flag == 0) { /* flag is off so everything is ok */
f = 0; /* turn f off to exit loop */
} else { /* flag is on, so get another number */
printf("Enter numerical char: "); /* flush stdout if no newline */
fflush(stdout);
flag = 0; /* turn your flag variable back off */
i = 0; /* reset i to 0 to check the next input */
}
}
/* let's see what we got in num */
printf("%s\n", num);
return 0;
}

Related

An Array of Arrays

I want to store a group of arrays containing 2 numbers in an array. But I only want 2 numbers to be stored when 5 followed by a comma and another number is entered. Essentially, what I want my program to do is read from this array of arrays and perform tasks accordingly. So if the user enters 2, I want to store (2,0) in one space of my array and move on to ask my user for the second number. But if the user types 5,10 I want the program to store (5,10) in that same array. Then my program could filter which array has only one value and which has 2 and do different tasks accordingly. My assignment requires us to not ask 2 numbers for each array which would have made it easier.
This is what I have so far and I know I'm wrong I just don't know where to go from here:
int main(void)
{
int size = 0;
int input;
int factor;
int mdArrays[100][2];
for (size_t i = 0; i < 100; i++)
{
size = i;
scanf("%d,%d", &input, &factor);
if (input != 5 && input != 9)
{
factor = 0;
for (size_t j =0 ; j< 2; j++)
{
mdArrays[i] = input;
mdArrays[j] = factor;
}
}
else if (input == 9)
{
break;
}
else
{
for(int j = 0; j< 2; j++)
{
mdArrays[i] = input;
mdArrays[j] = factor;
}
}
}
for (size_t i =0; i < size; i++)
{
for(size_t j = 0; j < 2; j++)
{
printf("%d,%d", mdArrays[i[j]]);
}
}
}
There were a few issues.
size is one too short (it should be i + 1).
It may be possible to handle 5 vs 5,23 using scanf. But, I prefer to use fgets and strtol and check the delimiter (e.g. whether it's , or not).
The if/else ladder logic can be simplified if we make the first test against input == 9 to stop the loop.
According to your code, you want to force a factor of zero if input != 5. That doesn't make much sense to me, but I've kept that logic [for now].
That may not be what you want/need, but it was my best interpretation of your code. The main purpose is to differentiate how many numbers are on a given line. So, adjust the rest as needed.
I think the way you're storing/displaying the array is incorrect. I believe you want to store input into mdArrays[i][0] and factor into mdArrays[i][1]. Using j makes no sense to me.
As I mentioned [in my top comments], the printf in the final loop is invalid.
Note that the code is cleaner if we don't hardwire the dimensions with a literal 100 in multiple places (e.g. once in the myArrays declaration and again in the outer for loop). Better to use (e.g.) #define MAXCOUNT 100 and replace 100 elsewhere with MAXCOUNT (see below).
I created three versions. One that is annotated with original and fixed code. Another that removes the original code. And, a third that organizes the data using a struct.
Here's the refactored code. I've bracketed your/old code [vs. my/new code] with:
#if 0
// old code
#else
// new code
#endif
I added a debug printf. Anyway, here's the code with some annotations:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(void)
{
int size = 0;
int input;
int factor;
int mdArrays[100][2];
for (size_t i = 0; i < 100; i++) {
#if 0
size = i;
scanf("%d,%d",&input,&factor);
#else
// get line
char buf[100];
char *cp = fgets(buf,sizeof(buf),stdin);
if (cp == NULL)
break;
// strip newline -- only needed for debug print
cp = strchr(buf,'\n');
if (cp != NULL)
*cp = 0;
// decode first number
input = strtol(buf,&cp,10);
// decode second number if it exists -- otherwise, use a sentinel
if (*cp == ',')
factor = strtol(cp + 1,&cp,10);
else
factor = -1;
printf("DEBUG: buf='%s' input=%d factor=%d\n",buf,input,factor);
#endif
// stop input if we see the end marker
if (input == 9)
break;
// remember number of array elements
size = i + 1;
// only use a non-zero factor if input is _not_ 5
if (input != 5) {
factor = 0;
#if 0
for (size_t j = 0; j < 2; j++) {
mdArrays[i] = input;
mdArrays[j] = factor;
}
continue;
#endif
}
#if 0
for (int j = 0; j < 2; j++) {
mdArrays[i] = input;
mdArrays[j] = factor;
}
#else
mdArrays[i][0] = input;
mdArrays[i][1] = factor;
#endif
}
for (size_t i = 0; i < size; i++) {
#if 0
for (size_t j = 0; j < 2; j++) {
printf("%d,%d",mdArrays[i[j]]);
}
#else
printf("%d,%d\n",mdArrays[i][0],mdArrays[i][1]);
#endif
}
return 0;
}
Here's the sample input I used to test:
5,3
7,6
8,9
5,37
5
9,23
Here's the program output:
DEBUG: buf='5,3' input=5 factor=3
DEBUG: buf='7,6' input=7 factor=6
DEBUG: buf='8,9' input=8 factor=9
DEBUG: buf='5,37' input=5 factor=37
DEBUG: buf='5' input=5 factor=-1
DEBUG: buf='9,23' input=9 factor=23
5,3
7,0
8,0
5,37
5,-1
Here's a slightly cleaned up version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXCOUNT 100
int
main(void)
{
int size = 0;
int input;
int factor;
int mdArrays[MAXCOUNT][2];
for (size_t i = 0; i < MAXCOUNT; i++) {
// get line
char buf[100];
char *cp = fgets(buf,sizeof(buf),stdin);
if (cp == NULL)
break;
// strip newline -- only needed for debug print
#ifdef DEBUG
cp = strchr(buf,'\n');
if (cp != NULL)
*cp = 0;
#endif
// decode first number
input = strtol(buf,&cp,10);
// decode second number if it exists -- otherwise, use a sentinel
if (*cp == ',')
factor = strtol(cp + 1,&cp,10);
else
factor = -1;
#ifdef DEBUG
printf("DEBUG: buf='%s' input=%d factor=%d\n",buf,input,factor);
#endif
// stop input if we see the end marker
if (input == 9)
break;
// remember number of array elements
size = i + 1;
// only use a non-zero factor if input is _not_ 5
if (input != 5)
factor = 0;
mdArrays[i][0] = input;
mdArrays[i][1] = factor;
}
for (size_t i = 0; i < size; i++)
printf("%d,%d\n",mdArrays[i][0],mdArrays[i][1]);
return 0;
}
You might benefit from using a struct [YMMV], so here's a version that keeps things organized that way:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXCOUNT 100
typedef struct {
int input;
int factor;
} data_t;
int
main(void)
{
int size = 0;
data_t mdArrays[MAXCOUNT];
data_t *data;
for (size_t i = 0; i < MAXCOUNT; i++) {
// get line
char buf[100];
char *cp = fgets(buf,sizeof(buf),stdin);
if (cp == NULL)
break;
// strip newline -- only needed for debug print
#ifdef DEBUG
cp = strchr(buf,'\n');
if (cp != NULL)
*cp = 0;
#endif
data = &mdArrays[i];
// decode first number
data->input = strtol(buf,&cp,10);
// decode second number if it exists -- otherwise, use a sentinel
if (*cp == ',')
data->factor = strtol(cp + 1,&cp,10);
else
data->factor = -1;
#ifdef DEBUG
printf("DEBUG: buf='%s' input=%d factor=%d\n",buf,input,factor);
#endif
// stop input if we see the end marker
if (data->input == 9)
break;
// remember number of array elements
size = i + 1;
// only use a non-zero factor if input is _not_ 5
if (data->input != 5)
data->factor = 0;
}
for (size_t i = 0; i < size; i++) {
data = &mdArrays[i];
printf("%d,%d\n",data->input,data->factor);
}
return 0;
}
Read a line of user input with fgets() and then parse it to see if it is one number, two comma separated numbers or something else.
I recommend using `"%n" to detect when and how scanning finished.
int get_numbers(int *input, int *factor) {
char buf[80];
if (fgets(buf, sizeof buf, stdin)) {
int n = 0;
sscanf(buf, "%d %n", input, &n);
if (n > 0 && buf[n] == '\0') return 1;
n = 0;
sscanf(buf, "%d ,%d %n", input, factor, &n);
if (n > 0 && buf[n] == '\0') return 2;
return 0; // Invalid input
}
return EOF; // No input
}
Usage
// scanf("%d,%d", &input, &factor);
switch (get_numbers(&input, &factor)) {
case 2: printf("%d %d\n", input, factor); break;
case 1: printf("%d\n", input); break;
case 0: printf("Invalid input\n"); break;
case EOF: printf("No input\n"); break;
}

Segmentation fault storing input in an array

I have written a C program that takes multiple inputs in the following format: rounds -> rows -> col -> array initial values
I have no issue with the following input: 2 2 2 0 0 0 0
but once put into the other format which looks like this
2
2 2
0 0
0 0
I get a segmentation fault, which I'm not sure why. I feel like I have newlines accounted for. Here is a snippet of my code where I think the error is happening:
int counter = 1;
char *num;
char *str;
long nums;
char line[BUFFER];
fgets (line, BUFFER, stdin);
num = strtok(line, " "); //getting first input that is separated by a space
int rounds = atoi(num); //changes input into a number
num = strtok(NULL, " ");
int row = atoi(num);
num = strtok(NULL, " ");
int col = atoi(num);
int grid[row][col];
for (int i = 0; i < row && !stop; i++){
for (int j = 0; j < col && !stop; j++){
num = strtok(NULL, " \n");
if (num == NULL){ //if the next character after whitespace is null/eof
printf("Invalid input 1");
stop = true;
}else{
nums = strtol(num, &str, 10);
if (*str != '\0' && *str != '\n'){
printf("Invalid input\n");
return 1;
}
int curr = nums;
grid[i][j] = curr;
}
}
}
EDIT: I realize the problem here now, but how would I distinguish if the input is put in the format of a single line vs if the input were on multiple lines so I could use fgets?
If one doesn't care about whitespace, one could completely decouple the input from the parsing. In this modified code, number takes a Buffer, and calls next_int; if this fails, (it will the first time because the buffer is empty,) the line buffer is refilled by calling next_line until it gets at least one number. In this way, it's kind of a singleton producer-consumer pattern.
#include <stdlib.h> /* EXIT malloc free strtol */
#include <stdio.h> /* printf fgets perror */
#include <assert.h> /* assert */
#include <errno.h> /* errno */
#include <string.h> /* strlen */
#include <limits.h> /* INT LONG */
#include <ctype.h> /* isspace */
/** Returns true if `line` was filled up, at most to `size` from `stdin`, thus
this limits the input we can get from `stdin` on top of `fgets`, (embedded
NULs don't work.) If false, `errno` will be set. */
static int next_line(char *line, const size_t size) {
assert(line && size > 2);
if(!fgets(line, size, stdin)) {
if(!ferror(stdin)) fprintf(stderr, "Not enough numbers.\n");
if(!errno) errno = EDOM; /* This is an error in this application. */
return 0;
}
if(line[0] == '\0' || line[strlen(line) - 1] != '\n')
return errno = ERANGE, 0; /* Line too long. */
return 1;
}
/** If EOF, line contained no non-whitespace characters, otherwise, if true,
`*input_ptr` is a whitespace and then number in the range of
`[MIN_INT, MAX_INT]`; it gets moved ahead that amount and `*output_ptr`
contains the number. If false, `errno` will be set. */
static int next_int(char **const input_ptr, int *const output_ptr) {
long l;
assert(input_ptr && output_ptr);
/* We could skip this, but then `errno` would possibly be set. */
while(isspace(**input_ptr)) (*input_ptr)++;
if(**input_ptr == '\0') return EOF;
/* Perform the conversion to `int` using a `long`. */
errno = 0;
l = strtol(*input_ptr, input_ptr, 0);
if((l == 0 || l == LONG_MAX || l == LONG_MIN) && errno) return 0;
if(l > INT_MAX || l < INT_MIN) return errno = ERANGE, 0;
*output_ptr = (int)l;
return 1;
}
/* `sizeof str` is the maximum line. */
struct Buffer { char str[256], *line; };
/** A true return means that we filled the buffer `in` as needed to get an
input `int` which is stored in `no`. Otherwise, `errno`. */
static int number(struct Buffer *const in, int *const no) {
int success;
assert(in && in->line >= in->str && in->line <= in->str + sizeof in->str);
while((success = next_int(&in->line, no)) == EOF) {
if(!next_line(in->str, sizeof in->str)) return 0;
in->line = in->str;
}
return success;
}
int main(void) {
int success = EXIT_FAILURE;
int row, col, *array = 0, i, j;
size_t n = 0, n_max;
struct Buffer in = { "", 0 };
in.line = in.str; /* Complete the `struct Buffer` contract. */
if(!number(&in, &row) || !number(&in, &col)) goto catch;
if(row <= 0 || col <= 0
|| (size_t)row > (size_t)-1 / col
|| (size_t)row * col > (size_t)-1 / sizeof *array)
{ errno = ERANGE; goto catch; } /* Overflow data types. */
n_max = row * col;
if(!(array = malloc(sizeof *array * n_max))) goto catch;
for(n = 0; n < n_max; n++) if(!number(&in, array + n)) goto catch;
for(j = 0; j < col; j++) {
for(i = 0; i < row; i++) {
printf("%s%4d", i ? ", " : "", array[j * row + i]);
}
fputc('\n', stdout);
}
success = EXIT_SUCCESS;
goto finally;
catch:
perror("array");
finally:
free(array);
return success;
}
Also changed,
strtol is used instead of atoi, which is equivalent, but it updates the pointer, has more error reporting, and can handle octal and hex.
C99 varible-length arrays are going to cause stack overflow if one makes them too big. In C17, they are optional. The code above uses malloc instead.

Type checking arbitrary length array in ANSI C

Hi I am confined to stdio.h, stdlib.h and string.h and I need to ask a user for input - the input can be any number of characters between 1 and 6, however the first two characters MUST be an uppercase alphabetical letter, and the remaining four characters MUST be a number between 0 and 9.
Examples of valid input:
AB1
AB1234
AB
A
Examples of Invalid Input:
AB12345 (too many characters)
123 (first two characters are not uppercase letters)
ABA (a character after the second one is not a numeric value)
Here is my attempt so far (just bear in mind I have almost no experience with C, the likelihood that this solution is "idiomatic" is next to none, and the reason I am asking this is so that I can learn):
Flightcode is a char array defined as flightcode[7] it lives inside another struct called flight. I am fgetsing it into a temp_array[7] first and then strcpying it into the flight->flightcode such that the null terminator is appended and I don't know a better way of doing that.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_FLIGHTCODE_LEN 6
#define MAX_CITYCODE_LEN 3
#define MAX_NUM_FLIGHTS 50
#define DB_NAME "database"
typedef struct {
int month;
int day;
int hour;
int minute;
} date_time_t;
typedef struct {
char flightcode[MAX_FLIGHTCODE_LEN + 1];
date_time_t departure_dt;
char arrival_city[MAX_CITYCODE_LEN + 1];
date_time_t arrival_dt;
} flight_t;
date_time_t departure_dt;
date_time_t arrival_dt;
char * scanline(char *dest, int dest_len);
int main(){
char temp_string[100];
flight_t flight[MAX_NUM_FLIGHTS + 1];
int correct_code = 0;
printf("Enter flight code>\n");
scanline(temp_string, sizeof(flight->flightcode));
strcpy(flight->flightcode, temp_string);
while(correct_code == 0)
{
for(int i = 0; flight->flightcode[i] != '\0' && correct_code == 0; i++)
{
while((i < 2 && (flight->flightcode[i] <= 64 || flight->flightcode[i] >= 91)) || (i > 1 && (flight->flightcode[i] < 48 || flight->flightcode[i] >= 58)))
{
printf("Invalid input.\n");
scanline(temp_string, sizeof(flight->flightcode));
strcpy(flight->flightcode, temp_string);
}
if((i < 2 && (flight->flightcode[i] > 64 || flight->flightcode[i] < 91)) || (i > 1 && (flight->flightcode[i] >= 48 || flight->flightcode[i] < 58)))
{
correct_code = 1;
}
}
}
}
char * scanline(char *dest, int dest_len){
int i, ch;
i = 0;
for (ch = getchar();
ch != '\n' && ch != EOF && i < dest_len -1; ch = getchar())
dest[i++] = ch;
dest[i] = '\0';
while (ch != '\n' && ch != EOF)
ch = getchar();
return (dest);
}
Scansets and the %n specifier could be used to parse the input.
The format string "%n%2[A-Z]%n%4[0-9]%n" uses the %n specifier in three places to capture the number of characters processed. The scanset %2[A-Z] will scan up to two characters if the characters are in the set of upper case letters. %4[0-9] will scan up to four characters if the characters are digits.
If two values are scanned by sscanf, the number of characters processed are subtracted to make sure there are two leading upper case characters and six or fewer total character and the trailing character is the terminating zero.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_FLIGHTCODE_LEN 6
#define MAX_CITYCODE_LEN 3
#define MAX_NUM_FLIGHTS 50
#define DB_NAME "database"
typedef struct {
int month;
int day;
int hour;
int minute;
} date_time_t;
typedef struct {
char flightcode[MAX_FLIGHTCODE_LEN + 1];
date_time_t departure_dt;
char arrival_city[MAX_CITYCODE_LEN + 1];
date_time_t arrival_dt;
} flight_t;
date_time_t departure_dt;
date_time_t arrival_dt;
char * scanline(char *dest, int dest_len);
int main(){
int head = 0, leading = 0, tail = 0;
int correct_code = 0;
int result = 0;
char temp_string[100];
char upper[3] = "";
char digits[5] = "";
flight_t flight[MAX_NUM_FLIGHTS + 1];
do {
printf("Enter flight code>\n");
scanline(temp_string, sizeof(temp_string));
if ( 0 < ( result = sscanf ( temp_string, "%n%2[A-Z]%n%4[0-9]%n", &head, upper, &leading, digits, &tail))) {
if ( 1 == result && 0 == temp_string[leading]) {
correct_code = 1;
break;
}
if ( 2 == result && 2 == leading - head && 7 > tail - head && 0 == temp_string[tail]) {
correct_code = 1;
}
else {
printf ( "invalid input\n");
}
}
else {
printf ( "invalid input\n");
}
} while(correct_code == 0);
printf ( "Input is: %s\n", temp_string);
strcpy(flight->flightcode, temp_string);
return 0;
}
char * scanline(char *dest, int dest_len){
int i, ch;
i = 0;
for (ch = getchar(); ch != '\n' && ch != EOF && i < dest_len -1; ch = getchar()) {
dest[i++] = ch;
}
dest[i] = '\0';
while (ch != '\n' && ch != EOF) {
ch = getchar();
}
return dest;
}
Your function scanline does not do much more than the standard function fgets. I propose to use the standard function instead. Removing the trailing newline '\n' is easy.
I have split the checks into 3 parts:
Check the length to be more than 0 and not more than MAX_FLIGHTCODE_LEN.
Check the first 2 characters to be uppercase letters A..Z
Check the remaining characters to be digits 0..9
Proposed code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_FLIGHTCODE_LEN 6
#define MAX_CITYCODE_LEN 3
#define MAX_NUM_FLIGHTS 50
#define DB_NAME "database"
typedef struct {
int month;
int day;
int hour;
int minute;
} date_time_t;
typedef struct {
char flightcode[MAX_FLIGHTCODE_LEN + 1];
date_time_t departure_dt;
char arrival_city[MAX_CITYCODE_LEN + 1];
date_time_t arrival_dt;
} flight_t;
date_time_t departure_dt;
date_time_t arrival_dt;
int main(void){
char temp_string[100];
flight_t flight[MAX_NUM_FLIGHTS + 1];
int correct_code;
size_t len;
int i;
do
{
/* we first assume the code is correct and set this to 0 on any error */
correct_code = 1;
printf("Enter flight code>\n");
if(fgets(temp_string, sizeof(temp_string), stdin) == NULL)
{
if(feof(stdin)) fprintf(stderr, "no input (EOF)\n");
else perror("fgets");
correct_code = 0;
temp_string[0] = '\0';
}
if(correct_code)
{
len = strlen(temp_string);
/* cut off newline
* Use a loop to handle CR and LF just in case Windows might leave more than one character */
while((len > 0) &&
((temp_string[len - 1] == '\n') ||
(temp_string[len - 1] == '\r')))
{
len--;
temp_string[len] == '\0';
}
if(len > MAX_FLIGHTCODE_LEN)
{
correct_code = 0;
fprintf(stderr, "Input must not be longer than %d characters.\n", MAX_FLIGHTCODE_LEN);
}
if(len == 0)
{
correct_code = 0;
fprintf(stderr, "Empty input.\n");
}
}
/* check first two letters */
for(i = 0; (i < 2) && (i < len) && correct_code; i++)
{
/* you could use function isupper when you make sure the locale is set to "C" */
if((temp_string[i] < 'A') || (temp_string[i] > 'Z'))
{
correct_code = 0;
fprintf(stderr, "first two characters must be uppercase letters. Found '%c' at position %d\n", temp_string[i], i);
}
}
/* check digits starting from 3rd character */
for(i = 2; (i < MAX_FLIGHTCODE_LEN) && (i < len) && correct_code; i++)
{
/* you could use function isdigit here */
if((temp_string[i] < '0') || (temp_string[i] > '9'))
{
correct_code = 0;
fprintf(stderr, "Third to last characters must be digits. Found '%c' at position %d\n", temp_string[i], i);
}
}
if(correct_code)
{
/* we already checked that length is not more than MAX_FLIGHTCODE_LEN, so we don't need strncpy to avoid buffer overflow */
strcpy(flight->flightcode, temp_string);
printf("Valid code: %s\n", flight->flightcode);
}
else
{
fprintf(stderr, "Invalid code.\n");
}
} while(!correct_code);
return 0;
}
You have a requirement that does not fit well with what scanf can easily do, so I would stay away from it, and use fgets as a primary read utility.
But as the number of acceptable uppercase and digit characters is not fixed by only limited I would use a custom parser based on a state machine. It is probably not the most elegant nor efficient way but it is simple, robust and easy to maintain.
Just to demonstrate it, I have allowed blank characters before the first uppercase one and spaces after the last digit. So the following code accept an arbitrary long line following this regex pattern [ \t]*[A-Z]{1,maxupper}[0-9]{0,maxdigit}\s* provided it receives a buffer of size at least maxupper+maxupper+1. It returns a pointer to the buffer is successful or NULL if not.
As you have said that you could not use the ctype macros, I have defined ASCII (or any charset derived from ASCII) equivalent for the ones I have used.
#define TRUE 1
#define FALSE 0
inline int isupper(int c) {
return c >= 'A' && c <= 'Z'; // only for ASCII and derived
}
inline int isdigit(char c) {
return c >= '0' && c <= '9'; // guarantee per standard
}
inline int isblank(int c) {
return c == ' ' || c == '\t';
}
inline int isspace(int c) {
static const char spaces[] = " \t\r\n\v";
for(const char *s=spaces; *s != '\0'; s++) {
if (c == *s) return TRUE;
}
return FALSE;
}
char *get_string(char *buffer, int maxupper, int maxdigit, FILE *fd) {
char buf[16]; // any size >=2 will fit
char *cur = buffer;
int state = 0, uppersize=0, digitsize=0;
for (;;) { // allow lines longer than buf
if (NULL == fgets(buf, sizeof(buf), fd)) {
*cur = '\0'; // EOF: do not forget the terminating NULL
return state >= 1 ? buffer : NULL; // must have at least 1 char
}
for (char *b=buf; *b!='\0'; b++) {
switch(state) {
case 0: // spaces before first uppercase
if (isblank(*b)) break;
state++;
case 1: // first uppercase
if (! isupper(*b)) {
state = 5; // must read up to \n
break;
}
state++;
case 2: // process uppercase chars
if (! isupper(*b)) {
if (uppersize > 0) state++;
else {
state = 5; // must read up to \n
break;
}
}
else {
if (uppersize >= maxupper) {
state = 5; // must read up to \n
break;
}
*cur++ = *b;
uppersize++;
break;
}
case 3: // process digit chars
if (! isdigit(*b)) {
state++;
}
else {
if (digitsize >= maxdigit) {
state = 5; // must read up to \n
break;
}
*cur++ = *b;
digitsize++;
break;
}
case 4: // allow spaces after last digit
if ('\n' == *b) {
*cur = '\0';
return buffer;
}
if (! isspace(*b)) state++
break;
case 5: // on error clean end of line
if ('\n' == *b) return NULL;
}
}
}
}
Then in your code, you simply calls it that way:
...
printf("Enter flight code>\n");
if (NULL == get_string(flight->flightcode, 2, 4, stdin)) {
// process the error
...
}
...
First thing, realize that your question text is missing a question. Moreover, your question title makes no sense.
Anyway, here it is a possible, purposely very ugly, solution. Approach: you want to do X, so you write the code to do X. Let's start with scanline():
int scanline(char *dest, int dest_len)
{
int i = 0;
int ch;
while (1) {
// Read
ch = fgetc(stdin);
// Check
if (ch == EOF)
break;
if (ch == '\n')
break;
if (i >= dest_len - 1)
break;
// Use
dest[i] = ch;
++i;
}
dest[i] = 0;
// Is the string finished? Ok!
if (ch == '\n' || ch == EOF)
return 1;
// Otherwise discard the rest of the line. Not ok!
while (ch != '\n' && ch != EOF)
ch = fgetc(stdin);
return 0;
}
I know this is ugly, but I believe that it is helpful to clarify the three steps involved in file input: read, check, use. Note that it returns true if the line was up to the required number of characters (one less than the buffer size to accomodate for the terminator.
Then you want to check if:
scanline() is successful
there is at least one character.
character 0 is between 'A' and 'Z'
character 1 is between 'A' and 'Z'
character 2 is between '0' and '1'
character 3 is between '0' and '1'
character 4 is between '0' and '1'
character 5 is between '0' and '1'
Lets write the code for that:
int main(void)
{
flight_t flight;
while (1) {
printf("Enter flight code>\n");
if (!scanline(flight.flightcode, sizeof(flight.flightcode))) {
printf("Too many characters.\n");
continue;
}
int i = 0;
if (flight.flightcode[i] == 0) {
printf("Empty input.\n");
continue;
}
if (flight.flightcode[i] < 'A' || flight.flightcode[i] > 'Z') {
printf("Character %d is not upper case.\n", i);
continue;
}
i++;
if (flight.flightcode[i] == 0)
break;
if (flight.flightcode[i] < 'A' || flight.flightcode[i] > 'Z') {
printf("Character %d is not upper case.\n", i);
continue;
}
i++;
if (flight.flightcode[i] == 0)
break;
if (flight.flightcode[i] < '0' || flight.flightcode[i] > '9') {
printf("Character %d is not a digit.\n", i);
continue;
}
i++;
if (flight.flightcode[i] == 0)
break;
if (flight.flightcode[i] < '0' || flight.flightcode[i] > '9') {
printf("Character %d is not a digit.\n", i);
continue;
}
i++;
if (flight.flightcode[i] == 0)
break;
if (flight.flightcode[i] < '0' || flight.flightcode[i] > '9') {
printf("Character %d is not a digit.\n", i);
continue;
}
i++;
if (flight.flightcode[i] == 0)
break;
if (flight.flightcode[i] < '0' || flight.flightcode[i] > '9') {
printf("Character %d is not a digit.\n", i);
continue;
}
i++;
if (flight.flightcode[i] == 0)
break;
}
}
Some remarks:
in your code you set correct_code to 1 as soon as the first character was ok. If you want to loop through the characters you must check if there is an error and exit the loop.
don't use ASCII codes when you have the specific character literals available.
I suggest that you take my solution and, as an exercise fix it to be able to work with arbitrary MAX_FLIGHTCODE_LEN, and possibly with arbitrary number of letters and numbers. Of course MAX_FLIGHTCODE_LEN shall be equal to their sum!
Drop the useless requirement for not using <ctype.h>, and use also <stdbool.h>, which makes the programmer intention clearer.

C Store number of a text file in an array

Hi i have this text document
Now i want to store only the numbers in an array how can i do it in c language??
www.google.com *** 64
www.victoria.org **** 118
www.example.org *** 120
This should do it:
#include <stdio.h>
// read in up to 10 lines
int LINES[10];
int main(){
int current = 0;
// open file for reading
FILE* file;
file = fopen("file.txt", "r");
// read whole file one char at a time
while (1){
char ch;
if(!fread(&ch, 1, 1, file)){
break;
}
if (ch >= '0' && ch <= '9'){
int val = ch - '0';
LINES[current] *= 10;
LINES[current] += val;
}else if (ch == '\n'){
current += 1;
}
}
// Looping over the results
for (int i = 0; i <= current; i += 1){
printf("Line %d = %d\n", i, LINES[i]);
}
}
There are multiple ways you can do this.
One way is to read the numbers into a temporary char[] array one character at a time with fgetc, then convert it to an int with the use of atoi().
To test if characters are integers, you can use the isdigit function from <ctype.h>, or you can simply test ch >= '0' && ch <= '9', either way works.
Here is some sample code:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define NUMLINES 10
#define NUMLEN 5
int main(void) {
FILE *fp;
int LINES[NUMLINES], i, count = 0, ch, blen = 0;
/* static temp buffer, make it bigger if integers will be more than 5 digits */
/* dynamic memory allocation always possible here */
char buffer[NUMLEN+1];
/* opening the file, with error checking */
fp = fopen("urls.txt", "r");
if (!fp) {
fprintf(stderr, "%s\n", "Error reading file");
return 1;
}
/* read each character until EOF */
while ((ch = fgetc(fp)) != EOF) {
/* digit found. add to temp buffer */
if (isdigit(ch)) {
buffer[blen++] = ch;
}
/* stop adding to buffer, now convert and add to array */
if (ch == '\n') {
buffer[blen] = '\0';
LINES[count++] = atoi(buffer);
blen = 0;
}
}
/* print the array */
for (i = 0; i < count; i++) {
printf("LINES[%d] = %d\n", i, LINES[i]);
}
return 0;
}
Which should output:
LINES[0] = 64
LINES[1] = 118
LINES[2] = 120

Convert a set of chars to int from a file

I'm reading:
22:5412:99:00 (...)
From a text file using (ch=fgetc(fp)) != EOF because I don't have only those numbers to read.
Identifying a number is easy with if(ch >= 48 && ch <= 57) but the thing is I want to put those numbers 22, 5412 into an array of integers. However when I read a char it reads part of number since each number is char.
It gets 2 (and not 22 like I want to) and in the next iteration reads the other 2. How can I save each set of numbers into it's own integer?
I hope I was clear enough, thanks!
My idea is to read in each char, and if it is a digit append it to a buffer. Whenever we get a non-digit, we just read the contents of the buffer as a string using sscanf, and clear the buffer for the next value.
#include <stdio.h>
#include <stdlib.h>
int read_buffer(char* buffer, int* sz)
{
int ret;
if (*sz==0) return 0;
buffer[*sz]='\0'; //end the string
sscanf(buffer,"%d", &ret); //read the contents into an int
*sz=0; // clear the buffer
return ret;
}
int main()
{
char buffer[1000];
int sz=0;
char ch;
FILE* input=fopen("input.txt","r");
// "input.txt" contains 22:5412:99:00
while ((ch=fgetc(input))!=EOF)
{
int number;
if (isdigit(ch))
{
buffer[sz++]=ch; // append to buffer
}
else
{
printf("Got %d\n",read_buffer(buffer,&sz)); // read contents of buffer and clear it
}
}
if (sz) // check if EOF occured while we were reading a number
printf("Got %d\n",read_buffer(buffer,&sz));
fclose(input);
return 0;
}
You would need to store the numbers as a string or a char* and use atoi to actually convert it to a number. http://www.cplusplus.com/reference/clibrary/cstdlib/atoi/
Assuming your pattern is of the type NN:NNNN:NN:NN, parse on the delimiter, feeding characters into a buffer:
int idx = 0, nIdx = 1;
int firstN, secondN, thirdN, fourthN;
char buf[5];
...
while ((ch=fgetc(fp)) != EOF) {
if (ch != ':') {
buf[idx++] = ch;
}
else {
buf[idx] = '\0';
idx = 0;
switch (nIdx++): {
case 1: firstN = atoi(buf); break;
case 2: secondN = atoi(buf); break;
case 3: thirdN = atoi(buf); break;
}
}
}
buf[idx] = '\0';
fourthN = atoi(buf);
...
I did a full program out of the previous post -- and some testing :-)
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
/* fill `array` with at most `siz` values read from the stream `fp` */
/* return the number of elements read */
size_t fillarray(int *array, size_t siz, FILE *fp) {
int ch;
size_t curr = 0;
int advance_index = 0;
while ((ch = fgetc(fp)) != EOF) {
if (isdigit((unsigned char)ch)) {
array[curr] *= 10;
array[curr] += ch - '0';
advance_index = 1;
} else {
if (advance_index) {
advance_index = 0;
curr++;
if (curr == siz) { /* array is full */
break;
}
}
}
}
return curr + advance_index;
}
int main(void) {
int array[1000] = {0};
int n, k;
n = fillarray(array, 1000, stdin);
if (n > 0) {
printf("%d values read:\n", n);
for (k=0; k<n; k++) {
printf(" %d", array[k]);
}
puts("");
} else {
fprintf(stderr, "no data read\n");
}
return 0;
}
And a test run
$ ./a.out
24555:76423 foobar 76235 jgfs(8) jhg x86-64 passw0rd RS232
[CTRL+D]
8 values read:
24555 76423 76235 8 86 64 0 232

Resources