演習5-14, 演習5-15, 演習5-16, 演習5-17 K&R プログラミング言語C

演習5-14

逆方向のソートを取り扱えるようにする。
my_qsort 関数に引数として reverse を追加し、比較の方向(<, >)を変化させる。

演習5-15

大文字・小文字の区別なしに比較できるようにする。
グローバル変数の fold を設定し、文字を比較する際に toupper で大文字に変換してから比較を行う。

演習5-16

文字、数字、空白についてのみ比較を行うようにする。
グローバル変数の dict_order を設定し、isalpha, isdigit, isspace で、文字、数字、空白以外をスキップして比較する。

演習5-17

フィールド単位で比較を行えるようにする。
フィールド比較関数の fieldcmp を定義し、グローバル変数 field_comp1 の場合は fieldcmp 関数を使い field_column_pos 番目のフィールドを比較する。

sort コマンドのコード

演習5-14 から 演習5-17 までを含んだ結果のコード

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAXLINES 5000       /* ソートすべき最大の行数 */
#define MAXLEN 1000         /* 任意の入力行の最大長 */
#define ALLOCSIZE 50000     /* 使用可能な場所の大きさ */
char *lineptr[MAXLINES];    /* テキスト行へのポインタ */
char allocbuf[ALLOCSIZE];   /* alloc のための記憶場所 */
char *allocp = allocbuf;    /* 次の空き位置(allocbuf の先頭アドレスを指して初期化) */

int numeric = 0;
int reverse = 0;
int fold = 0;
int dict_order = 0;
int field_comp = 0;
int field_column_pos = 0;

int my_getline(char *s, int max);
int readlines(char *lineptr[], int nlines);
void writelines (char *lineptr[], int nlines);
char *alloc(int n);
void my_qsort(void *v[], int left, int right,
        int (*comp)(void *, void *),
        int reverse);
int comp_direction(int, int);
int numcmp(char *, char *);
int my_strcmp(char *, char *);
int fieldcmp(char *, char *);

int main(int argc, char *argv[])
{
    int nlines, c;

    while (--argc > 0 && (*++argv)[0] == '-') {
        while ((c = *++argv[0])) {
            switch (c) {
            case 'n':
                numeric = 1;
                break;
            case 'r':
                reverse = 1;
                break;
            case 'f':
                fold = 1;
                break;
            case 'd':
                dict_order = 1;
                break;
            case 'e':
                field_comp = 1;
                field_column_pos = atoi(++(*argv));
                while (*++argv[0] != '\0')
                    ;
                --argv[0];
                break;
            default:
                fprintf(stderr, "sort : illegal option %c\n", c);
                exit(1);
            }
        }
    }

    if ((nlines = readlines(lineptr, MAXLINES)) >= 0) {
        my_qsort((void **)lineptr, 0, nlines-1,
                (int (*)(void *, void *)) (field_comp ? fieldcmp : (numeric ? numcmp : my_strcmp)),
                reverse);
        writelines(lineptr, nlines);
        return 0;
    } else {
        fprintf(stderr, "input too big to sort\n");
        exit(1);
    }
}

/* my_qsort : v[left] ... v[right] を昇順にソートする */
void my_qsort(void *v[], int left, int right,
        int (*comp)(void *, void *),
        int reverse)
{
    int i, last;
    void swap(void *v[], int, int);

    if (left >= right)  /* 配列の要素が2つより少なければ、何もしない */
        return;
    swap(v, left, (left + right)/2);
    last = left;
    for (i = left+1; i <= right; i++)
        if (comp_direction((*comp)(v[i], v[left]), reverse))
            swap(v, ++last, i);
    swap(v, left, last);
    my_qsort(v, left, last-1, comp, reverse);
    my_qsort(v, left+1, right, comp, reverse);
}

int comp_direction(int comp_result, int reverse)
{
    return (reverse > 0) ? (comp_result > 0) : (comp_result < 0);
}

/* numcmp : s1 と s2 を数値的に比較する */
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;
}

int my_strcmp(char *s1, char *s2)
{
    if (dict_order > 0) {
        while (!isalpha(*s1) && !isdigit(*s1) && !isspace(*s1) && *s1)
            s1++;
        while (!isalpha(*s2) && !isdigit(*s2) && !isspace(*s2) && *s2)
            s2++;
    }
    while ((fold > 0) ? (toupper(*s1) == toupper(*s2)) : (*s1 == *s2)) {
        if (*s1 == '\0')
            return 0;
        s1++;
        s2++;
        if (dict_order > 0) {
            while (!isalpha(*s1) && !isdigit(*s1) && !isspace(*s1) && *s1)
                s1++;
            while (!isalpha(*s2) && !isdigit(*s2) && !isspace(*s2) && *s2)
                s2++;
        }
    }
    return (fold > 0) ? (toupper(*s1) - toupper(*s2)) : (*s1 - *s2);
}

int fieldcmp(char *s1, char *s2)
{
    int i = 0;
    char p1[BUFSIZ];
    char p2[BUFSIZ];

    while (i < field_column_pos) {
        if (*s1 == ',' || *s1 == '\t')
            i++;
        if (*s1 == '\0') {
            fprintf(stderr, "error : too big %d than field column count\n", field_column_pos);
            exit(1);
        }
        s1++;
    }

    i = 0;
    while (*s1 != ',' && *s1 != '\0')
        p1[i++] = *s1++;
    p1[i] = '\0';

    i = 0;
    while (i < field_column_pos) {
        if (*s2 == ',' || *s2 == '\t')
            i++;
        if (*s2 == '\0') {
            fprintf(stderr, "error : too big %d than field column count\n", field_column_pos);
            exit(1);
        }
        s2++;
    }

    i = 0;
    while (*s2 != ',' && *s2 != '\0')
        p2[i++] = *s2++;
    p2[i] = '\0';

    return numeric ? numcmp(p1, p2) : strcmp(p1, p2);
}

/* swap */
void swap(void *v[], int i, int j)
{
    void *temp;

    temp = v[i];
    v[i] = v[j];
    v[j] = temp;
}

/* my_getline : s に行を入れ、長さを返す */
int my_getline(char *s, int lim)
{
    int c;
    char *ps;

    ps = s;
    while (--lim > 0 && (c = getchar()) != EOF && c != '\n')
        *s++ = c;
    if (c == '\n')
        *s++ = '\n';
    *s = '\0';

    return s - ps;
}

/* readlines : 入力行を読み込む */
int readlines(char *lineptr[], int maxlines)
{
    int len, nlines;
    char *p, line[MAXLEN];

    nlines = 0;
    while ((len = my_getline(line, MAXLEN)) > 0) {
        if (nlines >= maxlines || (p = alloc(len)) == NULL) {
            return -1;
        } else {
            line[len-1] = '\0'; /* 改行を消す */
            strcpy(p, line);
            lineptr[nlines++] = p;
        }
    }

    return nlines;
}

/* writelines : 出力行を書き出す */
void writelines(char *lineptr[], int nlines)
{
    int i;

    for (i = 0; i < nlines; i++)
        printf("%s\n", lineptr[i]);
}

/* n 文字へのポインタを返す */
char *alloc(int n)
{
    if (allocbuf + ALLOCSIZE - allocp >= n) { /* 入りきる */
        allocp += n;
        return allocp - n; /* 古い p */
    } else { /* 十分な空きがないとき */
        return 0; /* 異常事態を表わす 0 、通常は NULL (stdio.h で定義) を使う */
    }
}

/* p によって指される領域を解放する */
void afree(char *p)
{
    if (p >= allocbuf && p < allocbuf + ALLOCSIZE) {
        allocp = p;
    }
}

実行結果

$ cat numbers.csv
464,    8539,   9554
6398,   139,    239
912,    998,    1011
18,     512,    793
350,    30,     300
$ ./sort -e1 -n <numbers.csv
350,    30,     300
6398,   139,    239
18,     512,    793
912,    998,    1011
464,    8539,   9554
$ ./sort -e1 -nr <numbers.csv
464,    8539,   9554
912,    998,    1011
18,     512,    793
6398,   139,    239
350,    30,     300
$ ./sort -d <numbers.csv
18,     512,    793
350,    30,     300
464,    8539,   9554
6398,   139,    239
912,    998,    1011
プログラミング言語C 第2版 ANSI規格準拠
B.W. カーニハン D.M. リッチー
共立出版
売り上げランキング: 9726
«
»