The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 5.16 on page 121
Add the -d
("directory order") option, which makes comparisons only on letters, numbers
and blanks. Make sure it works in conjunction with -f
.
Solution by Barrett Drawdy
/* * Solution to K&R exercise 5-16 * This solution uses a slightly modified version of the readline function * that allows it to read very long lines. However, it can be used with * with the original readline. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> int getline(char*, int); char *alloc(int); int readlines(char**, int); void writelines(char**, int); void quicksort(void**, int, int, int (*)(void*, void*)); int numcmp(char*, char*); int mystrcmp(char*, char*); #define MAXLINES 10000 #define MAXLEN 1000 char *lineptr[MAXLINES]; /* pointers to text lines */ int decreasing = 0; /* 0 if increasing, 1 if decreasing -r flag */ int numeric = 0; /* 1 if numeric sort -n flag */ int fold = 0; /* 1 if not case-sensitive -f flag */ int directory = 0; /* 1 if directory sort -d flag */ int main(int argc, char* argv[]) { int nlines, i; while(--argc > 0) { ++argv; if((*argv)[0] == '-') for(i = 1; (*argv)[i]; ++i) switch((*argv)[i]) { case 'n': numeric = 1; break; case 'f': fold = 1; break; case 'r': decreasing = 1; break; case 'd': directory = 1; break; default: printf("usage: sort -dfnr\n"); return 1; } else { printf("usage: sort -dfnr\n"); return 1; } } if((nlines = readlines(lineptr, MAXLINES)) >= 0) { if(numeric) quicksort((void**) lineptr, 0, nlines - 1, (int (*)(void*, void*))numcmp); else quicksort((void**) lineptr, 0, nlines - 1, (int (*)(void*, void*))mystrcmp); writelines(lineptr, nlines); return 0; } else { printf("input too big to sort\n"); return 1; } } /* quicksort: sort v[left]...v[right] into increasing or decreasing order */ void quicksort(void *v[], int left, int right, int (*comp)(void*, void*)) { 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); /* move element to sort left */ last = left; for(i = left + 1; i <= right; ++i) { /* move all elements < or > sort */ if(!decreasing) { /* element to the left according */ if((*comp)(v[i], v[left]) < 0) /* to order */ swap(v, ++last, i); } else if((*comp)(v[i], v[left]) > 0) swap(v, ++last, i); } swap(v, left, last); /* move sort element to its final position */ quicksort(v, left, last - 1, comp); /* sort left subarray */ quicksort(v, last + 1, right, comp); /* sort right subarray */ } /* * mystrcmp: Compares s1 and s2 lexicographically. Ignores characters that * are not letters, numbers, or whitespace if directory flag is set. If the * fold flag is set, it isn't case sensitive. */ int mystrcmp(char *s1, char *s2) { if(directory) { while(!isdigit(*s1) && !isalpha(*s1) && !isspace(*s1) && *s1) ++s1; /* ignore bad characters */ while(!isdigit(*s2) && !isalpha(*s2) && !isspace(*s2) && *s2) ++s2; /* ignore bad characters */ } while(fold ? (tolower(*s1) == tolower(*s2)) : (*s1 == *s2)) { if(*s1 == '\0') return 0; ++s1; ++s2; if(directory) { while(!isdigit(*s1) && !isalpha(*s1) && !isspace(*s1) && *s1) ++s1; /* ignore bad characters */ while(!isdigit(*s2) && !isalpha(*s2) && !isspace(*s2) && *s2) ++s2; /* ignore bad characters */ } } return fold ? (tolower(*s1) - tolower(*s2)) : (*s1 - *s2); } /*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; } void swap(void *v[], int i, int j) { void *temp; temp = v[i]; v[i] = v[j]; v[j] = temp; } /*readlines: read input lines. This version is slightly modified to read longer lines than the limit set by MAXLEN. */ int readlines(char *lineptr[], int maxlines) { int len, nlines = 0; char *p, line[MAXLEN]; int longline = 0; while((len = getline(line, MAXLEN)) > 0) { if(nlines >= maxlines || (p = alloc(len)) == NULL) return -1; else { if(line[len - 1] == '\n') { line[len - 1] = '\0'; /* delete newline */ strcpy(p, line); if(!longline) lineptr[nlines++] = p; else longline = 0; } else { strcpy(p, line); if(!longline) { lineptr[nlines++] = p; longline = 1; } } } } return nlines; } /* writelines: write output lines */ void writelines(char *lineptr[], int nlines) { while(nlines-- > 0) printf("%s\n", *lineptr++); return; } int getline(char *s, int max) { int c; char *ps = s; while(--max && (c=getchar()) != EOF && c != '\n') *s++ = c; if(c == '\n') *s++ = '\n'; *s = '\0'; return s - ps; } #define ALLOCSIZE 2000000 static char allocbuf[ALLOCSIZE]; /* storage for alloc */ static char *allocp = allocbuf; /* next free position */ char *alloc(int n) { if(allocbuf + ALLOCSIZE - allocp >= n) { /* it fits */ allocp += n; return allocp - n; /* old p */ } else /* not enough room */ return 0; }
Solution by Codybartfast
(cat 0) -
Key based solution:
void dirvalue(char *value); void fold(char *value); char *makekey(char *value, int dofold, int dodirsort); 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); 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; int dodirsort = 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; else if (strcmp(argv[i], "-d") == 0) dodirsort = 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, dodirsort); 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; } void fold(char *value) { while ((*value = toupper(*value))) value++; } void dirvalue(char *value) { char c, *write; write = value; while ((c = *value++)) { if (isalnum(c) || c == ' ' || c == '\n') *write++ = c; } *write = '\0'; } char *makekey(char *value, int dofold, int dodirsort) { char *key = malloc((strlen(value) + 1) * sizeof(char)); strcpy(key, value); if (dofold) fold(key); if (dodirsort) dirvalue(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); }
Full code on github
Solution by anonymous
This is an extension from my exercise 5.15 solution. I added dircmp, referenced it in the pointer functions part of the code, and added -d to the arguments code.
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <ctype.h> /* Exercise 5-16. Add the -d ("directory order") option, which makes comparisons only on letters, numbers and blanks. Make sure it works in conjunction with -f. */ #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 *); int dircmp(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 int caseInsensitive = FALSE; // sort input lines int main(int argc, char *argv[]) { // nlines = number of input lines read, numeric, reverse, and dirOrder are flags for features turned on in the arguments int nlines, numeric = FALSE, reverse = FALSE, dirOrder = 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 if (strcmp(*argv, "-d") == 0) dirOrder = TRUE; else { printf("Usage: sort [-n] [-r] [-f] [-d]\n"); return 1; } baseCompare = (int (*)(void *, void *))(numeric ? numcmp : dirOrder ? dircmp : caseInsensitive ? strcmpi : strcmp); // chooses comparison function based on flags 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 } // compare s1 and s2 via directory order (compares only on letters, numbers, and blanks) int dircmp(const char *s1, const char *s2) { int i = 0, j = 0; while (s1[i] || s2[i]) if (isalnum(s1[i]) || isspace(s1[i])) if (isalnum(s2[j]) || isspace(s2[j])) if (caseInsensitive ? tolower(s1[i]) < tolower(s2[j]) : s1[i] < s2[j]) return -1; else if (caseInsensitive ? tolower(s1[i]) > tolower(s2[j]) : s1[i] > s2[j]) return 1; else i++, j++; else j++; else i++; return 0; }