読み込んだテキストファイルの配列化するには?

解決


ばかんす  2003-11-27 16:23:30  No: 52633  IP: [192.*.*.*]

はじめまして。
今、テキストファイルを読み込んで配列にしようとしていますが、上手い方法が
思いつきません。

ReadFile()でファイルの読み込みはできています。
ただ、読み込んだファイルを表示に適した配列にするのに悩んでいます。

xxx.txt内は、例えば−−−−−−−−−−−

一郎「明日は雨だ。」,次郎「なんでわかる。」,一郎「さぁ?」,
次郎「いいかげんな奴だな。」

−−−−−−−−−のように保存します。


これを、一つの文章ごとに
char* moji[4];
のような変数に入れていきたいんです。

*moji[0]には、

一郎「明日は雨だ。」

が格納されるといった感じです。

テキストの保存法等、改良した方がいい点があったらアドバイスお願いします。

編集 削除
YuO  2003-11-27 18:33:02  No: 52634  IP: [192.*.*.*]

> xxx.txt内は、例えば−−−−−−−−−−−
> 一郎「明日は雨だ。」,次郎「なんでわかる。」,一郎「さぁ?」,
> 次郎「いいかげんな奴だな。」
> −−−−−−−−−のように保存します。
> これを、一つの文章ごとに
> char* moji[4];
> のような変数に入れていきたいんです。
> *moji[0]には、
> 一郎「明日は雨だ。」
> が格納されるといった感じです。

まず,
*moji[0]
はchar型になるので,文字列は格納できません。
#moji[0]なら可能。

でもって,「一つの文章」とは何を単位にしているのですか?
xxx.txtから「一つの文章」を抜き出せと言われたら,私の場合
> 一郎「明日は雨だ。」,次郎「なんでわかる。」,一郎「さぁ?」,
> 次郎「いいかげんな奴だな。」
を抜き出しますが。

とりあえず,抜き出したい物の定義をはっきりさせてみて下さい。

編集 削除
ばかんす  2003-11-27 19:41:01  No: 52635  IP: [192.*.*.*]

説明不足、すみません。

一つの文章は、","で区切られた部分を意図しています。
この場合だと、
一郎「明日は雨だ。」
で一つ。
次郎「なんでわかる。」
で二つ目。
ということになります。

質問自体も、基本で申し訳ないです。

編集 削除
YuO  2003-11-27 21:16:46  No: 52636  IP: [192.*.*.*]

> 一つの文章は、","で区切られた部分を意図しています。

#それは普通「文」にあたるのでは……?

","が他に現れないのであれば,strtokを使うのが簡単だと思います。
会話文中に","が現れるというのであれば,
それを避けるように前からちゃんと調べる必要がありますが……。

編集 削除
YuO  2003-11-27 21:18:14  No: 52637  IP: [192.*.*.*]

つーか,
一行に一文,ってことを守ってファイル化してやればいいだけでは?
一行に複数の文を詰め込むから分解処理が発生するのだし。

編集 削除
ばかんす  2003-11-27 22:49:40  No: 52638  IP: [192.*.*.*]

どうもです。
実は、超初級ゆえ#moji[0]などは見慣れない型で正直とまどっています。
ですが、教えて君にならないよう自力でがんばってみます。

それから、あと日本語も勉強してきます…(笑)

編集 削除
ばかんす  2003-11-27 22:54:29  No: 52639  IP: [192.*.*.*]

って、もしかしてすごい勘違いしてたかも?
単なる
moji[0]
みたいですね…(汗)

編集 削除
なーめ  2003-11-29 18:26:53  No: 52640  IP: [192.*.*.*]

YuOさん>>
*moji[0]
はchar型になるので,文字列は格納できません。
そうですね、
しかも メモリの確保されていないポインタ(char *)の先の char です。

moji[0] = strdup( ... );

ですね。
ところで、ファイルから読み込むのをやめて、

char * g_arszText[] = 
{
   "一郎「明日は雨だ。」",
   "次郎「なんでわかる。」",
   ....
};

文単位で行になっていれば、
各行(")で括って、最後に(,)をつけるだけでCのソース(の一部)になりますが。
正規表現の置換でどうかな?

>> 変数に入れていきたいんです。

必ずしも、実行時に入れなくてもいいのではということです。
配列のサイズ(文の数)は

int g_nTextElements = ( sizeof( g_arszText ) / sizeof( char ));

で取れます。

ちなみに、NL(自然言語処理)ですか?

編集 削除
なーめ  2003-11-29 18:37:42  No: 52641  IP: [192.*.*.*]

閑話休題

  FILE * fp;
  char s[1024];
  char * moji[8192];
  int c = 0;
  if( fp = fopen( "data.txt", "r" ))
  {
     while( fgets( s, 1024, fp ))
     {
         moji[c++] = s;
         if( c == 8192 ) break;
     }
     fclose( fp );
  }

笑い話をCで語る。

編集 削除
ばかんす  2003-12-03 17:21:26  No: 52642  IP: [192.*.*.*]

なーめさん、レス遅くなってすいません。
自力でやろうとしてたもんで、しばらくここをチェックしていませんでした。

>char * g_arszText[] = 
>{
>   "一郎「明日は雨だ。」",
>   "次郎「なんでわかる。」",
>   ....
>};

実は最初にこの方法を使っていたのですが、あるサイトで「ソース内にこのように記述するのはいかにもバカっぽいので〜」の記述を見かけて、なんとかテキストから読み込もうとしていたのですが、背伸びしてもうまくいかないみたいです。

今、アドバイスを参考にして別のやり方に挑戦しています。

data.txt内で

>一郎「明日は雨だ。,間違いない!」
>次郎「なんでわかる。」
>一郎「さぁ?」
>次郎「いいかげんな奴だな。」

と記述し、

char moji[1024];
char **text;
の変数を用意し、

text=new char*[line];    //lineは、data.txt内の記述行数
text=new char[1024];
でメモリを確保します。

そして、

FILE* fp;
char* token;
int c=0;

if (!(fp=fopen("data.txt","r")))    //オープン
    return FALSE;

while (fgets(moji,1024,fp)) {    //読み込み
    //テキストの再編
    token = strtok(moji,",");
    while (token!=NULL) {
  strcat(text[c],token);
  token = strtok(NULL,",");
  if (token==NULL)
            break;
  strcat(text[c],"\n");
    }
    c++;
}

fclose(fp);  //クローズ

return TRUE;

として、mojiにテキストをひとまず格納した文字列を
","の部分で改行させて、実際の表示のために用意した**textに
入れようとしています。


これで、上手く表示されている時は、

>一郎「明日は雨だ。
>間違いない!」

と、text[0]には、data.txtの","部できちんと改行されて格納されますが、行数(テキスト)が進むにつれ、途中で余計な文字が表示されたり、前のテキストの文字が一部分表示されてしまいます。
努力してみたんですが、根本的にこのやり方自体がいただけないんでしょうか?
ちなみに、自然言語です。

編集 削除
YuO  2003-12-03 18:08:10  No: 52643  IP: [192.*.*.*]

> text=new char*[line];        //lineは、data.txt内の記述行数
> text=new char[1024];
> でメモリを確保します。

C++でやっているのであれば,newなんて使わずにstd::vectorとstd::string使った方が楽で確実です。

でもって,
上の二行目が滅茶苦茶です。
コンパイル時にエラーになるはずですが……。


> そして、
(snip)
> として、mojiにテキストをひとまず格納した文字列を
> ","の部分で改行させて、実際の表示のために用意した**textに
> 入れようとしています。

C++のライブラリをちゃんと使いましょう。

#include <fstream>
#include <istream>
#include <vector>
#include <string>

std::vector<std::string> text;

bool func (const char * filename) // 関数名は適当。C++組み込みのboolを利用。
{
    std::ifstream fin(filename);
    if (!fin.is_open()) return false;

    text.clear(); // textの中身をクリア

    std::string s;
    while (std::getline(fin, s)) { // 一行読み込み
        std::string::size_type pos0 = 0, pos;
        /* ,で分割。 */
        while (pos0 < s.length() && (pos = s.find(',', pos0)) != std::string::npos) {
            text.push_back(s.substr(pos0, pos - pos0) + "\n");
            pos0 = pos + 1;
        }
        if (pos0 < s.length()) {
            text.push_back(s.substr(pos0) + "\n");
        }
    }

    return true;
}

編集 削除
なーめ  2003-12-03 18:35:02  No: 52644  IP: [192.*.*.*]

>> なーめさん、レス遅くなってすいません。
>> 自力でやろうとしてたもんで、しばらくここをチェックしていませんでした。
いえいえ、
私もちょうどいいタイミングでした。
なにせ windows 2000 の再インストールしてたもので。

>>いかにもバカっぽいので〜
中級クラスにさしかかったばかりの人の意見でしょうな。
目的のコードをできるだけ早く仕上げることを求められることが
なかったのだと思います。仕事でコードを書いているとその
ような場面に出くわすことはよくあります。
ソースに文字列を組み込むのは使い捨てコードの場合で、
汎用性がまったくないものです。このようなプログラムを書くときは
超バカに徹して、さっくりとデータとりを行います。
そのようなプログラムは使い終わったら削除です。
見栄えにこだわって有効な手段をなきものにしてしまうのは惜しいです。

ところで、
>> 根本的にこのやり方自体がいただけないんでしょうか?
はい。
メモリの確保が足りません。


(1)
>> text=new char*[line];        //lineは、data.txt内の記述行数

(2)
>> text=new char[1024];
   ^^^^ --- moji ですね。
>> でメモリを確保します。

(1) はポインタ配列のメモリを確保しています。
(2) は fgets() の一時記憶域を確保しています。

変数 text は 1024 個のポインタの記憶域を確保しているだけであり、
文字列の記憶域を確保しているわけではありません。
なので、
(3) text[0] ... text[1023] の文字列記憶域を確保する必要があります。
for( c = 0; c < 1024; c ++ )
{
  text[c] = new char[1024];
}
... 下の例では strdup() を使って確保しています。

次に、
入力テキストが ","(半角、カンマ) で区切られているならば、strtok() が
使えますが、"。" (全角、句読点など)の場合は正しく動作しないと思われます。
strtok() のような ascii 系は 半角文字だけのデータには都合がいいのですが、
日本語処理を行うなら、役に立たない関数だと思っています。

それからね、
    fclose(fp); // クローズ
なんてコメントは入れるものではありません。
見ればわかるのです。他人が見てわからないかもしれないと思うところに
コメントを入れます。

私なら次のように書きます。
なお、コンパイル、テストはしていません。

  FILE * fp;
  //  次の s は moji に相当しますが、new する必要はなく、
  //  ローカルスタック宣言で十分です
  char s[1024];
  char bs[1024];
  char * p;
  char * q;
  int lines_used = 0;
  unsigned char b;

  bs[0] = 0;
  if( fp = fopen( "data.txt", "r" ))
  {
    while( fgets( s, 1024, fp ))
    {
      if( p = strchr( s, 0x0A )) *p = 0;  //  改行コード除去
      strcat( bs, s );
      for( p = q = s; *s; p ++ )
      {
        b = (unsigned char *)p;
        if( *p == ',' )
        {
          *p = 0;  //  カンマのところで文字列を切断する
          text[lines_used++] = strdup( q );
          q = p + 1;  //  切断した次の文字
        }
        else
        if(! strcmp( p, "。" ))
        {
          *p = 0;    //  句点のところで文字列を切断する
          text[lines_used++] = strdup( q );
          p ++;  //  漢字2バイト目をスキップ
        }
        else
        if(( 0x80 < b ) && ( b < 0xBF ) || ( 0xE0 <= b ) && ( b < 0xFD ))
        {
          p ++;  //  漢字2バイト目をスキップ
        }
      }
      for( q = bs; *p;)  //  未処理部分を前に詰めなおす
      {
        *q++ = *p++;
      }
      *q = 0;  //  ヌル終端
    }
    if( strlen( bs ))  //  最後の文をとり忘れないように!
    {
      text[lines_used++] = strdup( q );
    }

    fclose( fp );
    return( TRUE );
  }
  return( FALSE );

>> ちなみに、自然言語です。
私もでした。10年以上前だけど。

追伸、
確保したメモリを delete や free() するのを忘れないでね。

編集 削除
なーめ  2003-12-03 18:42:12  No: 52645  IP: [192.*.*.*]

>> C++でやっているのであれば,newなんて使わずにstd::vectorとstd::string使った方が楽で確実です。
確かにそうです。
でも時間的に、卒論(?)かけなくなってしまいそうですが。

編集 削除
なーめ  2003-12-04 00:52:20  No: 52646  IP: [192.*.*.*]

YuO さんのプログラムの先頭(#include の前)に1行追加。
#pragma warning( disable : 4786 )
デバッグ時のコンパイルで warning を抑止します。
一般にワーニングが出るといやになる方がおられると思うので、
老婆心ながら補足でした。
ワーニングの主旨はメッセージに出される名前が長すぎて、
TRACE() マクロ等の制限を越えるということです。

>> >> C++でやっているのであれば,newなんて使わずにstd::vectorとstd::string使った方が楽で確実です。
>> 確かにそうです。
>> でも時間的に、卒論(?)かけなくなってしまいそうですが。
前言撤回、失礼しました。

編集 削除
ばかんす  2003-12-04 23:54:51  No: 52647  IP: [192.*.*.*]

YuOさん、なーめさん、即レスありがとうございます。
滅茶苦茶な部分ですが、うつし間違いで書いていたコードはあっていました。
失礼しました。

>それからね、
>    fclose(fp); // クローズ
>なんてコメントは入れるものではありません。
>見ればわかるのです。他人が見てわからないかもしれないと思うところに
>コメントを入れます。
了解です。

> 確かにそうです。
> でも時間的に、卒論(?)かけなくなってしまいそうですが。
>前言撤回、失礼しました。
いいえ、事実自分でもそうだと思います。(笑)

それよりも、大変詳しいアドバイス・解説ありがとうございます。
とても勉強になりました。

それと、YuOさん。
そのコードでは、自力じゃないことがまるわかりになってしまいます。(笑)
ですが、お二人のアドバイスのおかげでなんとか解決できました。

まだまだ理解の及ばない箇所がありますが、これから勉強させてもらいます。
どうもありがとうございました。

編集 削除