オプション付きソートプログラム

K&R本 演習5-14、演習5-15

sortを変更して分類を逆方向にすることを指示する-rというフラグを取り扱えるようにせよ。-rは-nと同時に使えなければならない

分類において、大文字と小文字の区別をなくしてしまうためのオプション-fを加えよ。これによって、大文字と小文字のデータは一緒に分類されるから、aとAは、比較上は同じになる。

sort処理ですね。行を読み込む必要があるので、前回作ったreadlines関数をそのまま利用したいと思います。

ちなみにsort関連の演習5-16と5-17は設問の意味が良くわからなかったのでスルー。

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

#define LINEMAX 100
#define LINESMAX 5000

int strcmp_lower (char *s1,char *s2);
void swap(void **v,int i,int j);
int readlines (FILE *fh,char **lines,int size);
int mynumcmp (char *v1,char *v2);
int mynumcmpr (char *v1,char *v2);
int mystrcmp (char *a,char *b);
int mystrcmpr (char *a,char *b);
int mystrcmp_lower (char *a,char *b);
int mystrcmp_lowerr (char *a,char *b);
void myqsort(void *v[],int left,int right,int (*comp)(void *,void *));

void swap(void **v,int i,int j) {
    void *work;
    work = v[i];
    v[i] = v[j];
    v[j] = work;
}

int mynumcmp (char *v1,char *v2) {
    double a = atof(v1);
    double b = atof(v2);
    if ( a > b ) return 1;
    if ( a < b ) return -1;
    return 0;
}

int mynumcmpr (char *v1,char *v2) {
    double a = atof(v1);
    double b = atof(v2);
    if ( b > a ) return 1;
    if ( b < a ) return -1;
    return 0;
}

int mystrcmp (char *a,char *b) {
    return strcmp(a,b);
}

int mystrcmpr (char *a,char *b) {
    return strcmp(b,a);
}

int mystrcmp_lower (char *a,char *b) {
    return strcmp_lower(a,b);
}

int mystrcmp_lowerr (char *a,char *b) {
    return strcmp_lower(b,a);
}

// 大文字小文字区別しないstrcmp
int strcmp_lower (char *s1,char *s2) {
    while ( tolower(*s1) == tolower(*s2) ) {
        if ( *s1 == '\0' ) {
            return 0;
        }
        *s1++;
        *s2++;
    }
    return *s1 - *s2;
}

// K&Rに提示されているものを参考に書き換え
void myqsort(void *v[],int left,int right,int (*comp)(void *,void *)) {
    int i,last;
    
    if ( left >= right ) {
        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);
}

int readlines (FILE *fh,char **lines,int size) {
    int linenum = 0;
    size_t len;
    size_t linelen = 0;
    char str[LINEMAX];
    char *p = NULL;
    int add = 1;
    
    while( fgets(str,sizeof(str),fh) != NULL ) {
        linelen += len = strlen(str);
        add = (str[len-1] == '\n') ? 1 : 0;
        p = (char *)realloc(p,linelen+1);
        if ( p == NULL ) {
            return EOF;
        }
        strcpy_s(p+linelen-len,len+1,str);
        if ( add ) {
            if ( linenum == size ) { break; }
            lines[linenum++] = p;
            linelen = 0;
            p = NULL;
        }
    }
    
    return linenum;
}

int main (int argc,char *argv[]) {
    char *lines[LINESMAX];
    int linenum;
    int reverse = 0;
    int numeric = 0;
    int lower = 0;
    int i;
    int (*comp)(void *,void *) = mystrcmp;
    
    // コマンドラインオプションの処理
    if ( argc > 1 ) {
        while(--argc){
            if ( **++argv == '-' ) {
                if ( strlen(*argv) > 2 ) {
                    printf("unknown option: %s\n",++*argv);
                    break;
                }
                switch(*++*argv){
                    case 'f':
                        // 小文字にしてソートする
                        lower = 1;
                        break;
                    case 'n':
                        // 数値順にソートする
                        numeric = 1;
                        break;
                    case 'r':
                        // 逆順にソートする
                        reverse = 1;
                        break;
                    default:
                        printf("unknown option: %s\n",*argv);
                        break;
                }
            }
        }
    }
    
    // 比較関数の振り分け。ちょっと微妙?
    if ( reverse ) {
        if ( lower ) {
            comp = mystrcmp_lowerr;
        }
        else if ( numeric ) {
            comp = mynumcmpr;
        }
        else {
            comp = mystrcmpr;
        }
    }
    else {
        if ( lower ) {
            comp = mystrcmp_lower;
        }
        else if ( numeric ) {
            comp = mynumcmp;
        }
    }
    
    linenum = readlines(stdin,lines,LINESMAX);
    if ( linenum == EOF ) {
        puts("読み込みエラー");
        return 1;
    }
    
    myqsort(lines,0,linenum-1,comp);
    
    for(i=0;i<linenum;i++){
        printf("%s",lines[i]);
    }
    
    return 0;
}

これほど長いプログラムは今回が初めてですね。本格的って感じがします。

$ cat main.c | main -f -r
(中略)
void swap(void **v,int i,int j);
void swap(void **v,int i,int j) {
void myqsort(void *v[],int left,int right,int (*comp)(void *,void *));
void myqsort(void *v[],int left,int right,int (*comp)(void *,void *)) {
int strcmp_lower (char *s1,char *s2);
int strcmp_lower (char *s1,char *s2) {
int readlines (FILE *fh,char **lines,int size);
int readlines (FILE *fh,char **lines,int size) {
int mystrcmpr (char *a,char *b);
int mystrcmpr (char *a,char *b) {
int mystrcmp_lowerr (char *a,char *b);
int mystrcmp_lowerr (char *a,char *b) {
int mystrcmp_lower (char *a,char *b);
(中略)

とまあこのように複数のオプションの指定も可能です。