The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 5.15 on page 121
Add the option -f
to fold upper and lower case together, so that case distinctions are not made during sorting; for example, a
and A
compare equal.
Solution by Franz Fritsche
/* K&R Exercise 5-15 */ #include <stdio.h> #include <string.h> #include <ctype.h> #define MAXLINES 5000 /* max #lines to be sorted */ char *lineptr[MAXLINES]; /* pointers to text lines */ int readlines(char *lineptr[], int nlines); void writelines(char *lineptr[], int nlines); void my_qsort(void *lineptr[], int left, int right, int (*comp)(void *, void *), int order); int numcmp(char *, char *); int strcmp_f(char *, char *); /* sort input lines */ int main(int argc, char *argv[]) { int nlines; /* number of input lines read */ int numeric = 0; /* 1 if numeric sort */ int reverse = 0; /* 1 if sorting in reverse order */ int foldcase = 0; /* 1 if sorting case insensitive */ while (--argc > 0) { if (strcmp(*++argv, "-n") == 0) numeric = 1; else if (strcmp(*argv, "-r") == 0) reverse = 1; else if (strcmp(*argv, "-f") == 0) foldcase = 1; } if ((nlines = readlines(lineptr, MAXLINES)) >= 0) { my_qsort((void **) lineptr, 0, nlines-1, (int (*)(void *, void *))(numeric ? numcmp : (foldcase ? strcmp_f : strcmp)), reverse ? -1 : 1); writelines(lineptr, nlines); return 0; } else { printf("input too big to sort\n"); return 1; } return 0; } #define MAXLEN 1000 /* max length of any input line */ int getline(char *, int); char *alloc(int); /* readlines: read input lines */ int readlines(char *lineptr[], int maxlines) { int len, nlines; char *p, line[MAXLEN]; nlines = 0; while ((len = getline(line, MAXLEN)) > 0) if (nlines >= maxlines || (p = alloc(len)) == NULL) return -1; else { line[len-1] = '\0'; /* delete newline */ strcpy(p, line); lineptr[nlines++] = p; } return nlines; } /* writelines: write output lines */ void writelines(char *lineptr[], int nlines) { int i; for (i = 0; i < nlines; i++) printf("%s\n", lineptr[i]); } /* getline: read a line into s, return length */ int getline(char s[], int lim) { int c, i; for (i = 0; i < lim-1 && (c = getchar()) != EOF && c != '\n'; ++i) s[i] = c; if (c == '\n') s[i++] = c; s[i] = '\0'; return i; } #define ALLOCSIZE 10000 /* size of available space */ static char allocbuf[ALLOCSIZE]; /* storage for alloc */ static char *allocp = allocbuf; /* next free position */ char *alloc(int n) /* return pointer to n characters */ { if (allocbuf + ALLOCSIZE - allocp >= n) /* it fits */ { allocp += n; return allocp - n; /* old p */ } else /* not enough room */ return 0; } #include <stdlib.h> /* numcmp: compare s1 and s2 numerically */ int numcmp(char *s1, char *s2) { double v1, v2; v1 = atof(s1); v2 = atof(s2); if (v1 < v2) return -1; else if (v1 > v2) return 1; else return 0; } /* strcmp_f */ int strcmp_f(char *s, char *t) { for ( ; toupper(*s) == toupper(*t); s++, t++) if (*s == '\0') return 0; return toupper(*s) - toupper(*t); } /* my_qsort: sort v[left]...v[right] */ void my_qsort(void *v[], int left, int right, int (*comp)(void *, void *), int order) { int i, last; void swap(void *v[], int, int); if (left >= right) /* do nothing if array contains */ return; /* fewer than two elements */ swap(v, left, (left + right)/2); last = left; for (i = left+1; i <= right; i++) if (order * (*comp)(v[i], v[left]) < 0) swap(v, ++last, i); swap(v, left, last); my_qsort(v, left, last-1, comp, order); my_qsort(v, last+1, right, comp, order); } void swap(void *v[], int i, int j) { void *temp; temp = v[i]; v[i] = v[j]; v[j] = temp; }
Solution by codybartfast (Category 0)
This uses a different approach, it creates a 'key' from the line and then sorts on that key. So if folding, the key will be an upper case copy of the line. The orignal line, not the key, is printed.
char *fold(char *value); char *makekey(char *value, int dofold); char **keyvaluepair(char *key, char *value); char *key(char *pair[]); char *value(char *pair[]); int keycompare(void *a, void *b); int reverse(void *a, void *b); void freestuff(char ***kvms, int nlines); int (*basecompare)(void *, void *); int (*compare)(void *, void *); int main(int argc, char *argv[]) { int i, nlines; int numeric = 0; int doreverse = 0; int dofold = 0; char *buff, **lines, ***kvps; for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-n") == 0) numeric = 1; else if (strcmp(argv[i], "-r") == 0) doreverse = 1; else if (strcmp(argv[i], "-f") == 0) dofold = 1; } basecompare = numeric ? ((int (*)(void *, void *))numcmp) : ((int (*)(void *, void *))strcmp); compare = doreverse ? reverse : basecompare; if ((nlines = readlines(&buff, &lines)) == LNS_ERROR) { printf("input too big to sort\n"); return 0; } kvps = malloc(nlines * sizeof(char **)); for (i = 0; i < nlines; i++) { char *key = makekey(lines[i], dofold); kvps[i] = keyvaluepair(key, lines[i]); } quicksort((void **)kvps, 0, nlines - 1, keycompare); for (i = 0; i < nlines; i++) lines[i] = value(kvps[i]); writelines(lines, nlines); freelines(buff, lines); freestuff(kvps, nlines); return 0; } char *fold(char *value) { char *ptr = value; while ((*ptr = toupper(*ptr))) ptr++; return value; } char *makekey(char *value, int dofold) { char *key = malloc((strlen(value) + 1) * sizeof(char)); strcpy(key, value); if (dofold) fold(key); return key; } char **keyvaluepair(char key[], char value[]) { char **pair = malloc(2 * sizeof(char *)); pair[0] = key; pair[1] = value; return pair; } char *key(char *pair[]) { return pair[0]; } char *value(char *pair[]) { return pair[1]; } int keycompare(void *a, void *b) { char *v1 = key(a); char *v2 = key(b); return (*compare)(v1, v2); } int reverse(void *a, void *b) { return (*basecompare)(b, a); } void freestuff(char ***kvps, int nlines) { int i; for (i = 0; i < nlines; i++) { free(key(kvps[i])); free(kvps[i]); } free(kvps); }
Full code on github
Solution by anonymous
This is an extension from my exercise 5.14 solution. I simply added strcmpi in the pointer functions part of the code and -f handling in the argument code.
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <ctype.h> /* Exercise 5-15. Add the option -f to fold upper and lower case together, so that case distinctions are not made during sorting; for example, a and A compare equal. */ #define MAXLINES 5000 // max #lines to be sorted #define MAXLEN 1000 // max length of any input line #define ALLOCSIZE 10000 // size of available space char *lineptr[MAXLINES]; // pointers to text lines static char allocbuf[ALLOCSIZE]; // storage for alloc static char *allocp = allocbuf; // next free position int readlines(char *lineptr[], int nlines); void writelines(char *lineptr[], int nlines); void swap(void *v[], int, int); int readlines(char *lineptr[], int maxlines); void writelines(char *lineptr[], int nlines); int getline(char *s, int lim); char *alloc(int n); void myqsort(void *lineptr[], int left, int right, int (*comp)(void *, void *)); // qsort had to be renamed to myqsort since stdlib.h is included. int reverseCompare(void *, void *); // used to call baseCompare with the arguments in reverse order int numcmp(const char *, const char *); enum booelan { FALSE, TRUE }; // FALSE == 0, TRUE == 1, this creates booleans in a language that that doesn't natively support them unless you import stdbool.h in c99 or newer int (*baseCompare)(void *, void *); // function pointer called baseCompare which points to either numcmp or cmpstr depending on the sorting type int (*compare)(void *, void *); // function pointer called compare that will either point to the regular compare function baseCompare, or a new function called reverseCompare // sort input lines int main(int argc, char *argv[]) { // nlines = number of input lines read, numeric, reverse, and caseInsensitive are flags for features turned on in the arguments int nlines, numeric = FALSE, reverse = FALSE, caseInsensitive = FALSE; while (--argc > 0) if (strcmp(*++argv, "-n") == 0) numeric = TRUE; else if (strcmp(*argv, "-r") == 0) reverse = TRUE; else if (strcmp(*argv, "-f") == 0) caseInsensitive = TRUE; else { printf("Usage: sort [-n] [-r] [-f]\n"); return 1; } baseCompare = (int (*)(void *, void *))(numeric ? numcmp : caseInsensitive ? strcmpi : strcmp); // if numeric, use numcmp otherwise use strcmp/strcmpi compare = reverse ? reverseCompare : baseCompare; // if reverse, use reverseCompare, otherwise use baseCompare if ((nlines = readlines(lineptr, MAXLINES)) >= 0) { myqsort((void **) lineptr, 0, nlines - 1, compare); writelines(lineptr, nlines); return 0; } else { printf("input too big to sort\n"); return 1; } } // sort v[left]...v[right] into increasing order void myqsort(void *v[], int left, int right, int (*comp)(void *, void *)) { int i, last; if (left >= right) // do nothing if array contains fewer than two elements return; swap(v, left, (left + right) / 2); last = left; for (i = left + 1; i <= right; i++) if ((*comp)(v[i], v[left]) < 0) swap(v, ++last, i); swap(v, left, last); myqsort(v, left, last - 1, comp); myqsort(v, last + 1, right, comp); } // compare s1 and s2 numerically int numcmp(const char *s1, const char *s2) { double v1 = atof(s1), v2 = atof(s2); if (v1 < v2) return -1; else if (v1 > v2) return 1; return 0; } // swaps int i and j void swap(void *v[], int i, int j) { void *temp = v[i]; v[i] = v[j]; v[j] = temp; } // read input lines int readlines(char *lineptr[], int maxlines) { int len, nlines; char *p, line[MAXLEN]; nlines = 0; while ((len = getline(line, MAXLEN)) > 0) if (nlines >= maxlines || (p = alloc(len)) == NULL) return -1; else { line[len - 1] = '\0'; // delete newline strcpy(p, line); lineptr[nlines++] = p; } return nlines; } // write output lines void writelines(char *lineptr[], int nlines) { while (nlines-- > 0) printf("%s\n", *lineptr++); } // get line into s, return length int getline(char *s, int lim) { int c; char *original = s; while (--lim > 0 && (c = getchar()) != EOF && c != '\n') *s++ = c; if (c == '\n') *s++ = c; *s = '\0'; return s - original; // returns length } // return pointer to n characters char *alloc(int n) { if (allocbuf + ALLOCSIZE - allocp >= n) // it fits { allocp += n; // update marker to note where the next unallocated memory starts return allocp - n; // old pointer address } else return 0; // not enough room } int reverseCompare(void *left, void *right) { return (*baseCompare)(right, left); // reverses the order that items are compared and which causes it to be sorted in reverse (descending) order }