У вас есть требование, которое не вписывается в то, что легко может сделать scanf
, поэтому я бы держался подальше от него и использовал бы fgets
в качестве основной утилиты чтения.
Но в качестве числадопустимых заглавных и цифровых символов не фиксируется только ограниченным, я бы использовал собственный анализатор на основе конечного автомата.Это, вероятно, не самый элегантный и не эффективный способ, но он прост, надежен и прост в обслуживании.
Просто чтобы продемонстрировать это, я допустил пустые символы перед первой заглавной и пробелами после последней цифры.Поэтому следующий код принимает произвольную длинную строку, следующую этому шаблону регулярных выражений [ \t]*[A-Z]{1,maxupper}[0-9]{0,maxdigit}\s*
, при условии, что он получает буфер размером не менее maxupper+maxupper+1
.Он возвращает указатель на буфер успешно или NULL, если нет.
Поскольку вы сказали, что не можете использовать макросы ctype, я определил ASCII (или любой набор символов, производный от ASCII), эквивалентный тем, которые я
#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;
}
}
}
}
Тогда в своем коде вы просто называете это так:
...
printf("Enter flight code>\n");
if (NULL == get_string(flight->flightcode, 2, 4, stdin)) {
// process the error
...
}
...