C言語関係掲示板

過去ログ

No857 最大行と文字数を決めないで文字列の行ごとの読みこみと並べ替え

[戻る] [ホームページ]
No.11022

文字列の行ごとの読みこみと並べ替え
投稿者---senshou(2003/12/10 23:14:08)


「標準入力にてテキストファイルを行ごとに読みこみ、
それを辞書順に並べ替えて出力するプログラムを書け」

という課題で、以下のようなプログラムを提出したのですが、
「プログラマが勝手に最大行を決めてはいけない」といわれ、
返されてしまいました。
出来れば文字列の長さも勝手に決めないほうが良いそうです。

どこをどう直せばいいでしょうか?どなたか宜しくお願いします。

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

#define MAX_LINE 256    /* 最大行 */
#define MAX_LEN_OF_LINE 256 /* 行の最大文字列長 */

/* qsort()で使う */
int cmp(const void *s1, const void *s2)
{
  return strcmp(*(const char **)s1, *(const char **)s2);
}

/* 複製した文字列の先頭を指すポインタを返す関数 */
char *strdup(const char *s)
{
  char *p;

  p = malloc(strlen(s) + 1);    /* 文字列終端子の分、一つ多く領域確保 */
  if (p != NULL)    /* メモリ確保に成功した場合 */
    strcpy(p, s);   /* 文字列をコピーする */

  return p;
}

int main(void)
{
  char *str[MAX_LINE];
  char strtemp[MAX_LEN_OF_LINE];  /* 文字列を一時的に記憶するための配列 */
  int i, j;
 
  for (i = 0; fgets(strtemp, MAX_LEN_OF_LINE, stdin); i++) {  /* 標準入力から strtemp に一行読み込み、ファイルの終わりまでかエラーが発生した場合はループを終了させる */
    str[i] = strdup(strtemp);    /* str[i]に strtemp を複製 */
    str[i][strlen(str[i]) - 1] = '\0';    /* 文字列の最後にある'\n'を'\0'に置換 */
  }

  qsort((void *)str, i, sizeof(char *), cmp);  /* 文字列のソート */

  for (j = 0; j < i; j++) {  /* 入力された行の数だけ繰り返す */
    puts(str[j]);  /* 今までstrに格納し、ソートされたものを標準出力 */
    free(str[j]);  /* strdup()で確保した文字列の領域の解放 */
  }

  return 0;
}





No.11026

Re:文字列の行ごとの読みこみと並べ替え
投稿者---かずま(2003/12/11 03:25:04)


> 「プログラマが勝手に最大行を決めてはいけない」といわれ、
> 返されてしまいました。
> 出来れば文字列の長さも勝手に決めないほうが良いそうです。

C++ を使えば簡単なんですけどね。
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

int main()
{
    using namespace std;
    vector<string> lines;
    string buf;
    while (getline(cin, buf)) lines.push_back(buf);
    sort(lines.begin(), lines.end());
    copy(lines.begin(), lines.end(), ostream_iterator<string>(cout, "\n"));
}


No.11034

Re:文字列の行ごとの読みこみと並べ替え
投稿者---かずま(2003/12/11 11:10:06)


> C++ を使えば簡単なんですけどね。

訂正。
#include <iterator> を追加。

No.11039

Re:文字列の行ごとの読みこみと並べ替え
投稿者---かずま(2003/12/11 13:18:21)


> C++ を使えば簡単なんですけどね。

C で書き直してみました。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct { char **data; size_t size, capacity; } Vec;
typedef struct { char  *data; size_t size, capacity; } Buf;

void error(void) { fputs("out of memory\n", stderr); exit(1); }

Vec *vec_init(void)
{
    Vec *p = malloc(sizeof(Vec));
    if (p == NULL) error();
    p->size = 0;
    p->capacity = 256;
    p->data = malloc(p->capacity * sizeof(char *));
    if (p->data == NULL) error();
    return p;
}

Buf *buf_init(void)
{
    Buf *p = malloc(sizeof(Buf));
    if (p == NULL) error();
    p->size = 0;
    p->capacity = 256;
    p->data = malloc(p->capacity);
    if (p->data == NULL) error();
    return p;
}

void vec_add(Vec *vp, const char *s)
{
    if (vp->size == vp->capacity) {
        vp->capacity *= 2;
        vp->data = realloc(vp->data, vp->capacity);
        if (vp->data == NULL) error();
    }
    vp->data[vp->size] = strdup(s);
    if (vp->data[vp->size++] == NULL) error();
}

void buf_add(Buf *bp, int c)
{
    if (bp->size == bp->capacity) {
        bp->capacity *= 2;
        bp->data = realloc(bp->data, bp->capacity);
        if (bp->data == NULL) error();
    }
    bp->data[bp->size++] = c;
}

int getline(FILE *fp, Buf *bp)
{
    int c;
    bp->size = 0;
    while ((c = getc(fp)) != EOF && c != '\n') buf_add(bp, c);
    buf_add(bp, 0);
    return bp->size > 1 || c != EOF;
}

int comp(const void *a, const void *b)
{
    return strcmp(*(char **)a, *(char **)b);
}

int main(void)
{
    Vec *lines = vec_init();
    Buf *buf = buf_init();
    size_t i;

    while (getline(stdin, buf)) vec_add(lines, buf->data);
    qsort(lines->data, lines->size, sizeof(char *), comp);
    for (i = 0; i < lines->size; i++) puts(lines->data[i]);
    /* vec_free(vec); */
    /* buf_free(buf); */
    return 0;
}


No.11041

Re:文字列の行ごとの読みこみと並べ替え
投稿者---かずま(2003/12/11 13:53:07)


> C で書き直してみました。

訂正。
>   vp->data = realloc(vp->data, vp->capacity);

    vp->data = realloc(vp->data, vp->capacity * sizeof(char *));


No.11045

Re:文字列の行ごとの読みこみと並べ替え
投稿者---たか(2003/12/11 14:48:59)


面白いですね。私もすぐにC++のstd::string(std::getline使用)を思い
浮かべましたが、Cで桁数に限りを付けないというのはやった事がありま
せん。

考え方はSTLportのstd::vectorのように、バッファが足りなくなったら
倍々にrealloc()して行けばいいのですね。行方向はこれで思いついてい
ました。

こんな難しい問題を出すとは、その学校の先生、恐るべし!?

No.11047

Re:文字列の行ごとの読みこみと並べ替え
投稿者---かずま(2003/12/11 15:39:19)


> 考え方はSTLportのstd::vectorのように、バッファが足りなくなったら倍々に
> realloc()して行けばいいのですね。行方向はこれで思いついていました。

realloc() は、第1引数が NULL だと、malloc() の機能を兼ねるので、
もう少し短く書くこともできますね。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct { char **data; size_t size, capacity; } Vec;
typedef struct { char  *data; size_t size, capacity; } Buf;

void error(void) { fputs("out of memory\n", stderr); exit(1); }

void vec_add(Vec *vp, const char *s)
{
    if (vp->size == vp->capacity) {
        vp->capacity = vp->capacity ? vp->capacity * 2 : 256;
        vp->data = realloc(vp->data, vp->capacity * sizeof(char *));
        if (vp->data == NULL) error();
    }
    vp->data[vp->size] = strdup(s);
    if (vp->data[vp->size++] == NULL) error();
}

void buf_add(Buf *bp, int c)
{
    if (bp->size == bp->capacity) {
        bp->capacity = bp->capacity ? bp->capacity * 2 : 256;
        bp->data = realloc(bp->data, bp->capacity);
        if (bp->data == NULL) error();
    }
    bp->data[bp->size++] = c;
}

int getline(FILE *fp, Buf *bp)
{
    int c;
    bp->size = 0;
    while ((c = getc(fp)) != EOF && c != '\n') buf_add(bp, c);
    buf_add(bp, 0);
    return bp->size > 1 || c != EOF;
}

int comp(const void *a, const void *b)
{
    return strcmp(*(char **)a, *(char **)b);
}

int main(void)
{
    Vec lines = { 0 };  Buf buf = { 0 };  size_t i;

    while (getline(stdin, &buf)) vec_add(&lines, buf.data);
    qsort(lines.data, lines.size, sizeof(char *), comp);
    for (i = 0; i < lines.size; i++) puts(lines.data[i]);
    /* vec_free(&lines); */
    /* buf_free(&buf); */
    return 0;
}

> こんな難しい問題を出すとは、その学校の先生、恐るべし!?

問題を出した先生は解答を学生に示すべきだし、課題をこの掲示板で質問した
人は、その解答をまとめとして投稿してほしいものですね。

No.11091

Re:文字列の行ごとの読みこみと並べ替え
投稿者---senshou(2003/12/11 23:26:12)


>> こんな難しい問題を出すとは、その学校の先生、恐るべし!?

Cだとこんなに長く難しくなってしまうとは・・・

とりあえず言語の指定はないので、
一番授業で長く触れたCでやろうと思ったのですが・・・

C++を少し勉強してやってみたいと思います(来週までに間に合うかな・・・?)

本当にありがとうございました!
模範解答が出たら、報告したいと思います。

No.11129

Re:文字列の行ごとの読みこみと並べ替え
投稿者---ゆかり(2003/12/12 17:53:48)


>模範解答が出たら、報告したいと思います。

報告する前に先生の許可を得てくださいね。
著作権の問題があるので。

No.11142

Re:文字列の行ごとの読みこみと並べ替え
投稿者---senshou(2003/12/13 05:12:44)


>> C++ を使えば簡単なんですけどね。
>
>訂正。
>#include <iterator> を追加。

C++を独習し始め、なんとなく意味がわかってきました。

sort() 関数って何ソートを使っているかわかるでしょうか?
これがどこを見てもわからなくて・・・
qsort() ならばクイックソートだとわかっているのですが。


No.11144

Re:文字列の行ごとの読みこみと並べ替え
投稿者---YuO(2003/12/13 11:22:28)


>sort() 関数って何ソートを使っているかわかるでしょうか?

O(N log N)で実行できるソートであれば何でも良いことになっています。
例えば,VC++ 5.0付属のstd::sort (dinkumware製STL)だと,
数が多い内(17以上)についてはクイックソートを行い,
範囲が16以下になると挿入ソートを行っています。

これは,少ない要素数をソートする場合,クイックソートの方が定数項が大きいため,
逆に遅くなるからだと思います。


>qsort() ならばクイックソートだとわかっているのですが。

qsortは複雑さの規定すらありませんから,
内部でバブルソートを行っていても標準に適合します。


ちなみに,比較用の述語クラスをちゃんと作った場合,std::sortの方がqsortより速くなる可能性が高いです。
see) Effective STL 第46項 アルゴリズムのパラメータとして関数の代わりに関数オブジェクトの使用を考えよう


No.11145

Re:文字列の行ごとの読みこみと並べ替え
投稿者---たか(2003/12/13 13:21:57)


>>sort() 関数って何ソートを使っているかわかるでしょうか?

STLportでは「イントロソート」なる物を使っているようです。名称はと
もかくとして、YuOさんのおっしゃられるような方法が元になっている事
は間違いないでしょう。

実際に構造体などある程度大きさのあるオブジェクトでqsort()とstd::sort()
でベンチマークを取ってみると、std::sort()の方が3倍程度速くなります
(MinGW gcc3.3.1 + STLport 4.6)。

しかしchar型の配列でベンチを取るとqsort()とstd::sort()の差はなくな
ります。これは、std::sort()がテンプレート関数として書かれており、
代入演算子を使っているので、大きさのあるオブジェクトの転送が最適化
されたためだと考えられます。qsort()は実装にもよりますが、バイト単位
の転送を使っているものが多く、そのため実行速度に差が出たと考えられ
ます。

なお、STLportのstd::sort()はqsort()よりもスタックを多く消費するら
しく、同じ値をたくさん含む配列をソートするとstd::sort()はMinGW標準
のスタック領域では400000要素程度でスタックオーバーフローを起こしま
す。

No.11146

Re:文字列の行ごとの読みこみと並べ替え
投稿者---たか(2003/12/13 13:39:46)


論より証拠、試してみてください。汚いプログラムで済みません。
構造体の大きさを大きくするとstd::sort()もあまり速くなくなります。

#include <cstdlib>
#include <vector>
#include <ctime>
#include <iostream>
#include <algorithm>

const int N = 3000000;
int cmp(const void *, const void *);

struct Test {
  double d;
//  double dummy[1];
};

struct Test2 {
  int d;
//  double dummy[1];
};

typedef Test TYPE; // Choose structure type which shall be tested.

class cmp2 {
public:
  bool operator()(const TYPE& a, const TYPE& b) {
    if (a.d >= b.d) return true;
    else return false;
  }
};

int main()
{
  static TYPE d[N];
  std::vector<TYPE> vd;
  std::clock_t ti;
  
  std::srand(0);
  for (int i = 0; i < N; i++) d[i].d = std::rand() * RAND_MAX + std::rand();
  std::srand(0);
  for (int i = 0; i < N; i++) {
   TYPE t;
   t.d = std::rand() * RAND_MAX + std::rand();
   vd.push_back(t);
  }
  
  ti = std::clock();
  std::qsort(d, N, sizeof(TYPE), cmp);
  std::cout << "qsort = " << (std::clock() - ti) << std::endl;

  ti = std::clock();
  std::sort(vd.begin(), vd.end(), cmp2());
  std::cout << "sort = " << (std::clock() - ti) << std::endl;
}

int cmp(const void* a, const void* b)
{
  if (static_cast<const TYPE*>(a)->d > static_cast<const TYPE*>(b)->d) return -1;
  else if (static_cast<const TYPE*>(a)->d < static_cast<const TYPE*>(b)->d) return 1;
  return 0;
}