電子書籍の厳選無料作品が豊富!

さきほども上げたのですがカテゴリが間違っていたのでもう一回書き込みました

まだプログラムの勉強をはじめた初心者なのですが、

テキストファイルから文字を読みこみ、大文字ならば小文字に変換し辞書順に並びかえるプログラムを作っているのですがどうしてもうまくいきません。

例えばtest.txtに
XXX YYY YY XX
BBB aaa aa BB
とあれば
aa
aaa
bb
bbb
xx
xxx
yy
yyy
と表示されるよにしたいんです。

自分が作ったプログラむはこれです。
まだテキストファイルからでなくキーボードからの入力になっていますが・・・

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

int soto( const void *x, const void *y);

int main(int argc, char *argv[]){
FILE *input;
char str1[1000];

int i, j;

for (i = 1; i < argc; i++){

qsort(argv[i], 1000, sizeof( char *), soto);

strcpy(str1, argv[i]);

for(j = 0; j < 100; j++){
str1[j] = tolower( str1[j] );
}
printf("%s\n", str1);
}

return 0;
}



int soto( const void *a, const void *b){
char *x, *y;

x = (char*)a;
y = (char*)b;

return x-y;

}

これだと小文字にはなるんですがソートされずに表示されてしまいます・・・

どのようにすればいけるのかご指摘のほどおねがいします

A 回答 (5件)

この例だと、qsort()してから小文字変換しようとしてますね。


小文字にした状態で並べ替えるのなら、ソートの前にtolower()を全ての文字列に対し実行する必要があります。

それはさておき、qsort()関数のやってることに対する理解不足が、このプログラム開発を難しくしているようです。
まず最初に、qsort()を含めてsort系の関数は値を比較し、値の交換をして目的の並びへと変換します。
文字列は、文字という値の連続なので、そのままではソートできません。
つまり、[A][C][D][B][\0] という文字の並びを、[A][B][C][D][\0]と並べ替えることはできます。
しかし、[A][B][C][D][\0] , [A][A][A][B][C][\0] , [A][A][A][\0] という風に、
長さも内容も違う文字列を比較するのは
専用の関数を使う必要がありますし、文字列の交換は、qsort関数ではできません。
よって、一般的な対応策は次のようになります。
qsort()のコールバック関数soto()は、内部でstrcmpを使うか、同様の機能を実装する。
qsort()で並べ替える値は、文字列そのものでなく、文字列を指すポインタの配列、ポインタ配列を利用する。
ということです。
具体的な用法については、
http://www.geocities.jp/ky_webid/c/061.html
トップページは:http://www.geocities.jp/ky_webid/index.html
が詳しいと思います。
注意点として、このページの例ではchar* str_table[] = { "ccc", "ddd", "aaa", "bbb", "eee" };
という変数が用いられていますが、これは分かりやすく言うと、
「char型の、ポインタ配列str_tableを定義する。内容は"ccc", "ddd", "aaa", "bbb", "eee"の5つ」
となりますが、あなたの場合ポインタ配列の個数が未定義なので、大き目の固定長とするか、mallocで確保する必要があります
char *str_table[100];
int i;
for(i = 1; i < argc; i++){
 str_table[i] = argv[i];
}
そして、上記のように全ての文字列をポインタ配列に登録してやる必要があります。
がんばってください。
    • good
    • 0

> 例えばtest.txtに


> XXX YYY YY XX
> BBB aaa aa BB
> とあれば
> aa
> aaa
> bb
> bbb
> xx
> xxx
> yy
> yyy
> と表示されるよにしたいんです。

この要求を実現するには、
1.読み込む方法はどうするか。
2.読み込んだ文字列はどうメモリに格納するか。
3.大文字を小文字にどの段階で処理するのか。
4.ソートはqsort()を使うとして比較関数をどう使うか。
などの問題を決めて置かなければプログラムを組むことはできません。
このような現実に「初心者ではきつい」ように私も思います。

以下に回答例を示しますが、UNIX的リダイレクトを使って処理するものです。
Windowsの場合はリダイレクトに嫌悪感があるらしくほとんど使われないやり方ですが、どうしてもプロフラム中からtest.txtファイルを開いて行いたい場合は回答例に
fp=fopen("test.txt", "r")
などを加えれば動くように配慮したつもりです。このとき、fclose(fp)を忘れないでください。




/* UNIX的リダイレクト用のサンプル・プログラム */
#include <stdio.h> //for fscanf() etc.
#include <string.h> //for strcmp()
#include <stdlib.h> //for qsort()
#define SIZE 100 //行の大きさ
#define LEN 16 //列の大きさ

int compare(const void *x, const void *y){
return strcmp((char *)x, (char *)y);
}

int main(void){
char str[SIZE][LEN], *p; //SIZE行、LEN列の格納用文字配列strを用意
int j, k, n=0;
FILE *fp=stdin; //標準入力パスから読み込む
/* データの読み込み */
fscanf(fp, "%s %s %s %s", str[n], str[n+1], str[n+2], str[n+3]);
while(!feof(fp)){
printf("%s %s %s %s ", str[n], str[n+1], str[n+2], str[n+3]);
n += 4;
fscanf(fp, "%s %s %s %s", str[n], str[n+1], str[n+2], str[n+3]);
}
printf("\n");
/* 小文字へ変換 */
for(j=0; j<n; j++){ //j行目
for(k=0; str[j][k]; k++){ //k列の文字について
p=&str[j][k]; //文字のアドレスをセットし、
if('A' <= *p && *p <= 'Z') *p += 0x20; //補正
}
}
/* ソート */
qsort(str, n, LEN, compare);
/* ソート結果 */
for(j=0; j<n; j++){
printf("%s ", str[j]);
}
printf("\n");
return 0;
}


/* --- ./a.out < test.txt 実行結果 ---
XXX YYY YY XX BBB aaa aa BB
aa aaa bb bbb xx xxx yy yyy

Windowsの場合
○○○.exe < test.txt
*/
    • good
    • 0

全体的にいくつかの致命的な問題があります。


・qsort の使い方が違う
・文字列と文字が混同されている
などなど。

初心者には難しい課題だと思います。
参考までにコードを書いてみました。

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

#define MAX_ENTRIES 1000
#define DELIM " \t\r\n,;"

static void
strtolower ( char * s )
{
 while( *s ){ *s = tolower( *s ); s++; }
}

static int
strcmp_p ( const void * p1, const void * p2 )
{
 return strcmp( *(char * const *)p1, *(char * const *)p2 );
}

static void
read_words ( FILE * file, char *** p )
{
 char buf[4096];
 while( fgets( buf, sizeof buf, file ) ){
  char * token = strtok( buf, DELIM );
  if( token ) do {
   **p = strdup( token );
   strtolower( **p );
   (*p)++;
  } while( ( token = strtok( NULL, DELIM ) ) );
 }
}

extern int
main ( int argc, char ** argv )
{
 char ** words = malloc( sizeof (char *) * MAX_ENTRIES );
 char ** p = words;

 if( argc == 1 ){
  read_words( stdin, &p );
 }
 else while( 0 < --argc && *++argv ){
  FILE * file = fopen( *argv, "r" );
  if( ! file ){ perror( *argv ); continue; }
  read_words( file, &p );
  fclose( file );
 }

 qsort( words, p - words, sizeof (char *), strcmp_p );
 while( words < p ){ fprintf( stdout, "%s\n", *words++ ); }

 return EXIT_SUCCESS;
}
    • good
    • 0

くわしくは見てませんが、



> int soto( const void *a, const void *b){
> char *x, *y;

> x = (char*)a;
> y = (char*)b;

> return x-y;

> }

return x-y; は単にポインタの引き算をしているだけで、文字列の比較をしていることにはなりませんよ。
    • good
    • 0

qsort の動作を確認したほうがいいとは思うけど, 正直なところ本来の仕様を「初心者」が達成するのはつらいと思う.

    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!