改行の表現

[戻る]
閑古鳥 2005/04/30(土) 20:12:19
はじめまして。
Xpです。Cに関する質問ですが
fopenで読み込んだテキストファイルを一文字ずつ読み込んで
次に改行がきたかどうか判断する方法を教えてください。
改行された内容を別々の変数に格納したいのですが、
例えば
「test
file」
という内容の場合、
"test"と"file"を違う変数に格納したいのです。
お願いします。

Blue 2005/04/30(土) 20:17:57
fgetsで行単位で取得するのはどうでしょうか?

一文字づつならば '\n' かどうかで判定するのでしょうね。

参考WEBページ
http://www.geocities.jp/ky_webid/c/035.html

シャノン 2005/04/30(土) 20:23:53
> 次に改行がきたかどうか判断する方法を教えてください。

これは、test の最後の "t" を読み込んだ時点で、ということでしょうか?
それは不可能です。
ではどうすればよいか。簡単に言えば、もう1文字読めばいいだけです。

改行とは、改行文字という文字であることは認識されていますか?
test
file
という文字列は、test と file の間に改行文字が入っているのです。
具体的にいうと、それは(Windows の場合)0x0d 0x0a という2文字です。
つまり、これは
0x74 0x65 0x73 0x74 0x0d 0x0a 0x66 0x69 0x6c 0x65
という、10文字からなる文字列なわけです。

ですから、test の "t" の次にもう1文字読み込めば、file の "f" ではなく、0x0d が
読み込まれ、続いてもう1文字読めば、0x0a が読み込まれます。
これらが読み込まれたら、そこが改行だと判断できますので、0x0d の一つ前を文字列の
終端としていったん区切り、0x0a の次から、新たな文字列として扱えばいいということ
になります。

ところで、そのようなことをする標準関数は既に存在します。
fgets ならば、勝手に改行の位置を判断して、そこで読み込みを打ち切ってくれます。
次回の呼び出しは、改行の次から行われます。

また、同じことを自前でやるにしても、1文字ずつ読み込んでいくのは効率がよくあり
ません。
こういう場合は、あらかじめ何バイトかまとめて読み込んでおき、そのなかから改行文
字を探すという手法が一般的かと思われます。

シャノン 2005/04/30(土) 20:24:35
肝心なことをいい忘れました。

C 言語では、0x0d は '\r'、0x0a は '\a' と書きます。

シャノン 2005/04/30(土) 20:25:03
ごめんなさいまた間違えました。

0x0a は '\n' です。

閑古鳥 2005/05/01(日) 20:43:59
Blueさん、シャノンさん。
分かりやすい説明をありがとうございます。

自分の作るテキストファイルはどんな内容でどの位のサイズにも
対応できるようにしたいので、
fgetsだと読み込むテキストのサイズが必要ですから
サイズが分からない以上まとめて読み込むしかないと思います。
全て読み込んだ後に分割して変数に格納できるのならいいですけど
可能でしょうか?

ちなみにfgetcで一文字ずつ読み込んで文字列に格納する場合
char buf[3][1024]; 

while((ch=fgetc(fp))!=EOF)
{ 
if(ch=='\n') {
buf[cnt2++][cnt]='\0';
cnt=0;
}
else{buf[cnt2][cnt++]=ch;}
} 
buf[cnt2][cnt] = '\0'; 
という感じで読み込み部分を行っているのですが
上手くいきません。どうすればいいでしょうか?
説明下手ですみませんが、どうかご教授お願いします。

Blue 2005/05/01(日) 21:51:15
私が提示されたソースで動かしたところうまくいきましたが、
(読み込むテキストは3行で1行1024文字以下(当然3行目の行末には改行文字はな
い))
どのようなところがうまくいかないのでしょうか?

>char buf[3][1024]; 
の時点でサイズがきまっているようなのですが。。。

fgetsは一行の文字数でよいので、一行1024文字以下とわかっている場合は
いけるとおもいますが。

それすらわからない場合は、ある程度一気に読み込み、改行文字の有無でさらに読み込
むような処理になります。(シャノンさんがすでにその方法を挙げています。)

その場合に動的に一行の文字数領域を取らないといけないのでカナリ苦労するかと。

C++でもいいならばSTLライブラリの std::string,std::getline,std::vector などを
駆使すれば楽そうだが。。。

RiSK [E-Mail] 2005/05/01(日) 22:29:10
こういう事がやりたい?
# STL の getline については知りませんが…

- test.txt
test
file

- GetLine.c
#include <stdio.h>
#include <stdlib.h>

char * GetLine(FILE * f)
{
    int c;
    size_t size = 0, capacity = 0;
    char * p = NULL, * tmp;
    while ((c = getc(f)) != EOF && c != '\n') {
        if (size >= capacity) {
            capacity = capacity ? capacity * 2 : 32;
            if ((tmp = realloc(p, capacity)) == NULL) {
                free(p);
                return NULL;
            }
            p = tmp;
        }
        p[size++] = (char)c;
    }
    p[size] = '\0';
    return p;
}

int main(void)
{
    char * p, * q;
    FILE * f = fopen("test.txt", "r");
    if (f == NULL) return 1;
    p = GetLine(f);
    q = GetLine(f);
    puts(p);
    puts(q);
    free(p);
    free(q);
    fclose(f);
    return 0;
}

- 結果
test
file
Press any key to continue

閑古鳥 2005/05/03(火) 16:28:51
[[解決]]
すみません。自己解決しました。
どうやら前述のコードの省略されてた部分で
軽いエラーというか手違いがありまして、
問題なく変数に格納できていました。
char buf[3][1024]; 
int i , ch, cnt = 0, cnt2 = 0; 
FILE *fp; 
if ((fp = fopen("test.txt", "r")) == NULL) 
{ 
printf("error\n"); 
exit(1); 
} 

while((ch=fgetc(fp))!=EOF)
{ 
if(ch=='\n') {
buf[cnt2++][cnt]='\0';
cnt=0;
}
else buf[cnt2][cnt++]=ch;
} 
buf[cnt2][cnt] = '\0'; 
fclose(fp);
for(i = 0; i < cnt2+1; i++) 
printf("%s\n", buf[i]);
こんな感じですが…。

>>char buf[3][1024]; 
>の時点でサイズがきまっているようなのですが。。。
今はこれで十分だと思い、あえてサイズを決めておきました。

まあ率直に申しますと
文字列のサイズを不定にする方法が分からなかったのですが…。

>RiSKさん
お手数かけさせて申し訳ありません。
ソースを解読できませんが、
お気持ちだけでも受け取っておきます。

Blue 2005/05/03(火) 16:45:04
参考までに、
>C++でもいいならばSTLライブラリの std::string,std::getline,std::vector などを
>駆使すれば楽そうだが。。。
のソースを載せておきます。
ふ〜んC++だとこんな風にかけるんだ程度に捕らえてもらって結構ですので。

#include <string>
#include <iostream>
#include <fstream>
#include <vector>

int main( void )
{
    std::vector< std::string > vecTextData;
    std::string   strLine;
    std::ifstream finData( "test.txt" );

    if ( !finData.is_open() ) return EXIT_FAILURE;

    // ファイルより取得
    while ( std::getline( finData, strLine ) )
    {
        vecTextData.push_back( strLine );
    }
    finData.close();

    // 内容の表示
    std::vector< std::string >::const_iterator it = vecTextData.begin();
    std::vector< std::string >::const_iterator itend = vecTextData.end();
    while ( it != itend )
    {
        std::cout << *it++ << std::endl;
    }
    return EXIT_SUCCESS;
}

環境 OS         : WindowsXp SP2 Pro
     コンパイラ : VC++6.0   SP6 Stand

古いスレッドには追加発言できません。