The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 7.04 on page 159
Write a private version of scanf
analogous to minprintf
from the previous section.
Solution by Thomas Amundsen
It seems that the real scanf doesn't handle floats and strings the way I expect it to. Having said that, the book's example of a rudimentary calculator on page 141 does not work on my machine.
Here is a solution that only handles integers.
/* Thomas Amundsen - K&R2 Exercise 7-4 - 2009-06-19 */ #include <stdio.h> #include <stdarg.h> void minscanf(char *fmt, ...); int main() { int i; minscanf("%d", &i); /* scan integer from stdin */ printf("scanned %d\n", i); /* print scanning results to stdout */ return 0; } /* minscanf: minimal scanf with variable argument list only scans integers */ void minscanf(char *fmt, ...) { va_list ap; /* points to each unnamed arg in turn */ char *p; int *ival; va_start(ap, fmt); /* make ap point to 1st unnamed arg */ for (p = fmt; *p; p++) { /* skip chars that aren't format conversions */ if (*p != '%') continue; /* prev char was %, look for format conversion */ switch(*++p) { case 'd': ival = va_arg(ap, int *); /* get integer pointer from args */ scanf("%d", ival); /* read integer into int pointer */ break; default: break; } } }
Solution by codybartfast (cat 0)
Supports %d, %f, %s and %%.
#include <ctype.h> #include <stdarg.h> #include <stdlib.h> int minscanf(char *str, char *fmt, ...); int minscanf(char *str, char *fmt, ...) { va_list ap; int matched = 0; char *p; va_start(ap, fmt); for (; *fmt; fmt++) { if (isspace(*fmt)) continue; for (; isspace(*str); str++) ; if (*str == '\0') break; if (*fmt != '%') { if (*fmt == *str) { str++; continue; } else { break; } } switch (*++fmt) { case 'd': if (!isdigit(*str) && !((*str == '+' || *str == '-') && isdigit(*(str + 1)))) break; *va_arg(ap, int *) = atoi(str); str++; while (isdigit(*str) && *str) str++; matched++; continue; case 'f': *va_arg(ap, double *) = strtod(str, &p); if (p == str) break; str = p; matched++; continue; case 's': if (isspace(*str) || !*str) break; p = va_arg(ap, char *); while (!isspace(*str) && *str) *p++ = *str++; *p = '\0'; matched++; continue; case '%': if (*str == '%') { str++; continue; } else { break; } default: break; } break; } va_end(ap); return matched; }
Solution by anonymous
I implemented all of the basic format specifiers for C that I could find except for %n. Also, I didn't include support for field width, length modifiers, assignment suppression, and the [...] and [^…] character sets.
#include <stdio.h> #include <stdarg.h> /* Exercise 7-4. Write a private version of scanf analogous to minprintf from the previous section. */ int minscanf(char *fmt, ...); int main() { char s[3], c; int i1, i2, i3, i4, i5, i6, i7, i8, i9, i10; float f1, f2, f3, f4, f5; unsigned int u; void *p = NULL; printf("Copy and paste in the following text to test this program:\n-5 0x6 0X7 7 010 11 11 c D 0xe 0XFg hi 1.920e+001 2.122E+001 2.3 2.4 2.5 %p %%\n", (void *) &c); if (minscanf("%d %i %i %i %o %o %u %x %x %x %x %c %s %e %e %f %g %g %p %%", &i1, &i2, &i3, &i4, &i5, &i6, &u, &i7, &i8, &i9, &i10, &c, s, &f1, &f2, &f3, &f4, &f5, &p) != 19) printf("Not all inputs were assigned!\n"); printf("%d %#x %#X %o %#o %o %u %x %X %#x %#X%c %s %.3e %.3E %.1f %g %g %p %%\n", i1, i2, i3, i4, i5, i6, u, i7, i8, i9, i10, c, s, f1, f2, f3, f4, f5, p); return 0; } // minimal scanf with variable argument list. Returns number of assigned input items int minscanf(char *fmt, ...) { va_list ap; // points to each unnamed arg in turn char *p, *sval, format[3] = { '%', '\0', '\0' }; int *ival, numFound = 0; double *dval; unsigned int *uival; void **vval; // pointer to a pointer va_start(ap, fmt); // make ap point to 1st unnamed arg for (p = fmt; *p; p++) { if (*p != '%') continue; switch (*++p) { case 'd': case 'i': case 'c': // char is promoted to int when passed through '...' ival = va_arg(ap, int *); format[1] = *p; numFound += scanf(format, ival); break; case 'e': case 'f': case 'g': dval = va_arg(ap, double *); format[1] = *p; numFound += scanf(format, dval); break; case 's': sval = va_arg(ap, char *); numFound += scanf("%s", sval); break; case 'x': case 'o': case 'u': uival = va_arg(ap, unsigned int *); format[1] = *p; numFound += scanf(format, uival); break; case 'p': vval = va_arg(ap, void *); numFound += scanf("%p", vval); case '%': // no need to do anything break; default: printf("unsupported format specifier: %%%c\n", *p); break; } } va_end(ap); // clean up when done return numFound; }