掲示板利用宣言

 次のフォームをすべてチェックしてからご利用ください。

 私は

 題名と投稿者名は具体的に書きます。
 課題の丸投げはしません。
 ソースの添付は「HTML変換ツール」で字下げします。
 返信の引用は最小限にします。
 環境(OSとコンパイラ)や症状は具体的に詳しく書きます。
 返信の付いた投稿は削除しません。
 マルチポスト(多重投稿)はしません。

掲示板2

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧

No.26095

配列
投稿者---ぴよ(2006/02/13 00:57:11)


初歩的なことで大変申し訳ないのですが、
このサイトをいろいろと参考にさせて頂き、
b.txtのアドレス部分0xから始まる数字部分8桁を
別ファイルに書き込むようにしました。
addr[9]の配列ですと、'\0'も含めて
addr[11]でないと入らないと思いますが、
なぜエラーにもならないのでしょうか?
かなり初歩的なことだと思いますが申し訳ありませんが
いろいろと調べましたが、よく分かりませんでしたので、
よろしくお願いします。

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

main ()
{
    FILE *fin, *fout;
    char infile[256], outfile[256], str[256], addr[9];
    char *inp = infile;
    int i, j, k;
    
    if ( ( fin = fopen( "b.txt","r" ) ) == NULL )
    {
        printf ( "infile open error\n" );
        exit( 1 );
    }
    if ( ( fout = fopen( "addr.txt","w" ) ) == NULL )
    {
        printf ( "outfile open error\n" );
        exit( 1 );
    }
    while ( fgets( inp, 256, fin ) != NULL )
    {
        for ( i = 0; *( inp + i ) != '\0'; i++ )
        {
            if ( *( inp + i ) == '0' && *( inp + i + 1 ) == 'x' )
            {
                addr[0] = '0';
                addr[1] = 'x'; // x = 0x78
                for ( j = 2, k = 1; j < 10; j++, k++ )
                {
                    addr[j] = *( inp + i + 1 + k );
                }
                addr[j] = '\0';
                strcat ( addr , "\n");
                fprintf ( fout, "%s", addr );
            }
        }
    }
    fclose ( fin );
    fclose ( fout );
    
    return;
}

b.txtの中身
fffdss0xa1aa3334trr
fggg0xbb445563
a.txtの結果
0xa1aa3334
0xbb445563



この投稿にコメントする

削除パスワード

発言に関する情報 題名 投稿番号 投稿者名 投稿日時
<子記事> Re:配列 26096 επιστημη 2006/02/13 01:11:33


No.26096

Re:配列
投稿者---επιστημη(2006/02/13 01:11:33)


>addr[9]の配列ですと、'\0'も含めて
>addr[11]でないと入らないと思いますが、
>なぜエラーにもならないのでしょうか?

Cは配列の上限をチェックしないから。
'たまたま'動いているだけです。
極めて危険なコードです。



この投稿にコメントする

削除パスワード

No.26195

Re:配列
投稿者---ぴよ(2006/02/19 23:05:37)


ご返信ありがとうございました。

>Cは配列の上限をチェックしないから。
>'たまたま'動いているだけです。
>極めて危険なコードです。

存じてませんでした。ありがとうございます。
私が記述したコードなんか効率が悪いような気がしますが、
効率がよい方法はないのでしょうか?
お手数ですが、よろしくお願いします。




この投稿にコメントする

削除パスワード

No.26196

Re:配列
投稿者---επιστημη(2006/02/19 23:37:57)


>私が記述したコードなんか効率が悪いような気がしますが、
>効率がよい方法はないのでしょうか?

どこがですか? なぜですか?



この投稿にコメントする

削除パスワード

No.26197

Re:配列
投稿者---Blue(2006/02/19 23:45:11)


あえて言うなら
>strcat ( addr , "\n");
>fprintf ( fout, "%s", addr );
でしょうか?

単に
fprintf( fout, "%s\n", addr );

でよさげ。

ただ、fgets で読み込むと末尾改行文字が付くので注意が必要そう。


それと、仕様が良く分からないのですけど。
例えば入力として

0x123450xABCDEF

とあったら、今のコードで動きは正しいのですか?
一行には 0x の記述は 一つないしゼロということでしょうか?



この投稿にコメントする

削除パスワード

No.26198

Re:配列
投稿者---Blue(2006/02/19 23:52:20)


追記:

見易さを考えるのならば、strncmp や strncpy を使われると良いです。
ただ、処理的に早くはなるとはいえませんが。
(strstrで検索したほうがよいのかなぁ。。。)


この投稿にコメントする

削除パスワード

No.26199

Re:配列
投稿者---επιστημη(2006/02/20 00:15:54)


>見易さを考えるのならば、strncmp や strncpy を使われると良いです。
>ただ、処理的に早くはなるとはいえませんが。
>(strstrで検索したほうがよいのかなぁ。。。)

strstr使ってみた。 Blueさんが懸念されている仕様上の不明な点は考慮していません。

#include <assert.h>

int main() {
  FILE* fin;
  FILE* fout;
  char infile[256];

  fin = fopen("b.txt", "r");
  assert( fin );
  fout = fopen("addr.txt", "w");
  assert( fout );

  while ( fgets(infile, 256, fin) != NULL ) {
    char* start;
    char* pos;
    start = infile;
    while ( (pos = strstr(start, "0x")) != NULL ) {
      char address[11];
      strncpy(address, pos, 10);
      address[10] = '\0';
      fprintf(fout, "%s\n", address);
      start = pos + 1;;
    }
  }

  fclose(fin);
  fclose(fout);

  return 0;
}





この投稿にコメントする

削除パスワード

No.26202

Re:配列
投稿者---ぴよ(2006/02/20 01:18:21)


επιστημηさん、Blueさんありがとうございました。
大変勉強になりましたし、
ソースが短くなったことがよく分かりました。
お二人のを参考にし、下記のようにしました。
コンパイルできるのですが、落ちて、addr.txtにデータが
入りません。なぜでしょうか?
お手数ですが、よろしくお願いします。

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

#define BYTE_2 8

int main ()
{
    FILE *fin, *fout;
    char infile[256];
    int i, j, t, count;
    
    fin = fopen("b.txt", "r");
    assert( fin );
    fout = fopen("addr.txt", "w");
    assert( fout );
    
    while ( fgets( infile, 256, fin ) != NULL )
    {
        char *start;
        char *inp;
        char *temp;
        start = infile;
        while ( ( inp = strstr(start, "0x")) != NULL ) {
            count = 0;
            // 16進数評価
            for ( t = 2; t < 10; t++ )
            {
                if ( isxdigit( *( inp + t ) ) != 0 )
                {
                    count++;
                }
            }
            // 16進数のみ代入
            if ( count == BYTE_2 )
            {
                char  addr[11];
                temp = tolower( *inp );
                strncpy(addr, temp, 10);
                addr[10] = '\0';
                fprintf(fout, "%s\n", addr);
            }
            start = inp + 1;
        }
    }
    
    fclose ( fin );
    fclose ( fout );
    
    return 0;
}



この投稿にコメントする

削除パスワード

No.26203

Re:配列
投稿者---Blue(2006/02/20 02:12:42)


> for ( t = 2; t < 10; t++ )
> {
>     if ( isxdigit( *( inp + t ) ) != 0 )
>     {
>         count++;
>     }
> }
ここって、格納された文字数以上に見ている可能性がないでしょうか?

for ( t = 2; t < 10; t++ )
{
    if ( isxdigit( *( inp + t ) ) == 0 )
    {
        break; // ここで調査の打ち切り
    }
    count++;
}

> temp = tolower( *inp );
は何をさせたいのでしょうか?
標準関数の tolower だと、char* 型ではなく char型の値を返すはずですけど。
(しかも先頭の文字のみ小文字になる。)
# といかここでコンパイルエラーはでなかったのかな?
全て小文字で格納したいならば、strncpy関数を使う利点はないです。
普通に for 文で小文字にした値を代入していくことになるでしょう。



この投稿にコメントする

削除パスワード

No.26234

Re:配列
投稿者---ぴよ(2006/02/21 00:58:42)


ありがとうございます。
お蔭様で使わせて頂き、下記の通りにしましたら、
コンパイルは通りましたが、
// 16進数評価
for ( t = 2; t < 10; t++ )
に入りませんでした。
if ( isxdigit( *( inp + t ) ) != 0 )
の部分で1文字を評価していないからだと思いますが、
ポインタを使用し、1文字評価するのはどのようにしたら
よいのでしょうか?
> ここって、格納された文字数以上に見ている可能性がないでしょうか?
> if ( isxdigit( *( inp + t ) ) == 0 )
というのは見つかった1文字が
16進以外の文字が出てきたら打ち切りという意味ではなかったのでしょうか?はじめの1文字が16進数だったら打ち切りという意味だったのでしょうか?


    while ( fgets( infile, 256, fin ) != NULL )
    {
        char *start;
        char *inp;
        char *temp;
        start = infile;
        while ( ( inp = strstr(start, "0x") ) != NULL )
        {
            count = 0;
            // 16進数評価
            for ( t = 2; t < 10; t++ )
            {
                if ( isxdigit( *( inp + t ) ) != 0 )
                {
                    break;
                }
                count++;
             }
            // 16進数のみ代入
            if ( count == BYTE_2 )
            {
                char  addr[11];
                for ( i = 2; i < 11; i++ )
                {
                    strncpy(addr, inp, 2);
                    addr[i] = tolower( *( inp + i ) );
                }
                addr[11] = '\0';
                fprintf(fout, "%s\n", addr);
            }
            start = inp + 1;
        }
    }

> temp = tolower( *inp );
> は何をさせたいのでしょうか?
> 標準関数の tolower だと、char* 型ではなく char型の値を返すはずですけど。

変更しました。「char* 型ではなく char型の値を返すはず」なのですが、
上記のようにしたらなぜエラーにならないのでしょうか?

お手数ですが、よろしくお願いします。



この投稿にコメントする

削除パスワード

No.26237

Re:配列
投稿者---Blue(2006/02/21 01:36:02)


> // 16進数評価
> for ( t = 2; t < 10; t++ )
> に入りませんでした。
ここはどのようにして確認しましたか?デバッガ?
# 環境を明示しておくべきです。OSやコンパイラ等。(この情報がないと説明するのが遠回りになる)

>> 標準関数の tolower だと、char* 型ではなく char型の値を返すはずですけど。
ここ間違えていました。tokowerはchar型ではなく、int型の値を返しますね。

説明が面倒なので下のソースを参考にしてみてください。
未コンパイル(もしかしたら動かないかも)

while ( fgets( infile, sizeof( infile ), fin ) ) { char* start = infile; char* inp; while ( ( inp = strstr( start, "0x" ) ) ) { /* 16進数であるかチェック */ int t; int count = 0; for ( t = 2; t < 10; t++ ) { if ( !isxdigit( *( inp + t ) ) ) { /* 16進数としてつかえない文字があった */ break; } count++; } /* 16進数文字が連続で8つ出てきたら */ if ( count == BYTE_2 ) { char addr[ 11 ]; int i; strncpy( addr, inp, 2 ); /* 小文字にして格納 */ for ( i = 2; i < 10; i++ ) { addr[ i ] = ( char )tolower( *( inp + i ) ); } addr[ 10 ] = '\0'; fprintf( fout, "%s\n", addr ); } start = inp + 1; } }



この投稿にコメントする

削除パスワード

No.26285

Re:配列
投稿者---ぴよ(2006/02/27 01:16:38)


使用させて頂きお蔭様で出来ました。
しかし機能を追加したらまたおかしくなりました。

b.txtの内容は
ffafdssTOTL :0xa1aa3334trr0xDE651122treMAX :0x12gettts0x123AEDF4TOTL :0xaef12345
fggg0xbb445563

です。
TOTLとMAXの16進数は読み飛ばし、
TOTLとMAX以外の16進数をaddrに出力したいのですが、
1列目MAX前の16進数が表示されず、
1列目最後のTOTLの16進数も表示されて落ちます。
いろいろと考えましたがよく分かりませんでした。
大変お手数ですが、よろしくお願いします。

>>// 16進数評価
>> for ( t = 2; t < 10; t++ )
>> に入りませんでした。
>ここはどのようにして確認しましたか?
printfで確認しました。ボーランドCです。

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

#define BYTE_2 8

int main(void)
{
    FILE *fin, *fout;
    char infile[256];
    
    fin = fopen("b.txt", "r");
    assert( fin );
    fout = fopen("addr.txt", "w");
    assert( fout );
    
    while ( fgets( infile, sizeof( infile ), fin ) )
    {
        char *start;
        char *inp;
        char countSM1[11];
        char countSM2[11];
        int u, f, j ,i;
        start = infile;
        for ( j = 0; *( start + j ) != '\0'; j++ )
        { 
            // TOTL MAX の文字があったら後の16進数は入れない
            if ( ( inp = strstr( start, "TOTL :" ) ) )
            {
                start = inp + 16;
                printf ( "start1 = %s", inp );
                if ( ( inp = strstr( start , "MAX :" ) ) )
                {
                    start = inp + 15;
                    printf ( "start2 = %s", inp );
                    if ( ( inp = strstr( start , "0x" ) ) )
                    {
                        int t;
                        int count = 0;
                        printf ( "start3 = %s", inp );
                        // 16進数評価
                        for ( t = 2; t < 10; t++ )
                        {
                            if ( !isxdigit( *( inp + t ) ) )
                            {
                                break;
                            }
                            count++;
                        }
                        // 16進数のみ代入
                        if ( count == BYTE_2 )
                        {
                            char  addr[11];
                            strncpy(addr, inp, 2);
                            for ( i = 2; i < 10; i++ )
                            {
                                addr[i] = ( char )tolower( *( inp + i ) );
                            }
                            addr[10] = '\0';
                            printf ( "addr = %s", addr );
                            fprintf(fout, "%s\n", addr);
                        }
                    }
                }
            }
            start = inp + 1;
        }
    }
    
    fclose ( fin );
    fclose ( fout );
    
    return 0;
}



この投稿にコメントする

削除パスワード

No.26286

Re:配列
投稿者---Blue(2006/02/27 01:42:03)


最初のほうで指摘しましたが、仕様が微妙です。

> ffafdssTOTL :0xa1aa3334trr0xDE651122treMAX :0x12gettts0x123AEDF4TOTL :0xaef12345
> fggg0xbb445563
のとき結果的にどのような値が取れればいいのでしょうか?

1) TOTL :0x0x1234567890
2) TOTLT :0x1234567890
3) MMAX :0x1234567890
4) MAx: 0x1234567890
5) MAX:0x1234567890
6) TOTL :TOTL0x1234567890

それぞれどうなりますか?

それと、main文が少し長くなってきたので、関数化を考えるのはどうでしょうか?
fgets以降のブロックを関数にする。
引数としては、fgetsで取得した文字列を渡す。

具体的にプロトタイプ宣言は
void showAddr( const char* linebuff );
のような感じ。


この投稿にコメントする

削除パスワード

No.26287

Re:配列
投稿者---Blue(2006/02/27 02:09:17)


>それと、main文が少し長くなってきたので、関数化を考えるのはどうでしょうか?
>fgets以降のブロックを関数にする。
>引数としては、fgetsで取得した文字列を渡す。
>
>具体的にプロトタイプ宣言は
>void showAddr( const char* linebuff );
>のような感じ。
ごめんなさい。これはダメでしたね。結果をファイルに書き込むからこの宣言じゃだめですね。(関数名もダメだし)
# コンソール画面に出すだけかと勘違いしていた。

まぁ、いずれにせよ処理を大きく分けて考えると見やすいしわかりやすくなると思います。
どのように分けるかはその人のセンスですかねぇ?


この投稿にコメントする

削除パスワード

No.26407

Re:配列
投稿者---ぴよ(2006/03/12 23:21:18)


遅くなりました。
どうもありがとうございました。

>1) TOTL :0x0x1234567890
>2) TOTLT :0x1234567890
>3) MMAX :0x1234567890
>4) MAx: 0x1234567890
>5) MAX:0x1234567890
>6) TOTL :TOTL0x1234567890

これはすべて読み飛ばします。

>void showAddr( const char* linebuff );
>のような感じ。

>ごめんなさい。これはダメでしたね。結果をファイルに書き込むからこの宣言じゃだめですね。(関数名もダメだし)

どうもありがとうございます。
コンソール画面に出すにせよ、
この関数をどう使ったらいいのかイメージが
沸かないでいます。

>まぁ、いずれにせよ処理を大きく分けて考えると見やすいしわかりやすくなると思います。

だんだん難しくなって来ました。
ちょっと考えてみます。


この投稿にコメントする

削除パスワード

No.26412

Re:配列
投稿者---Blue(2006/03/13 09:23:42)


>3) MMAX :0x1234567890
これがNGだと、かなり難しいようですけど。
何をもって、トークンとするのかとう明確にしてください。

ABCMMAX :0x12 ---> NG
ABC MAX :0x12 ---> ?
treMAX :0x12  ---> ?

# 流石に半月たたないとレスがつかないと、やる気なくなるなぁ、、、



この投稿にコメントする

削除パスワード

No.26554

Re:配列
投稿者---ぴよ(2006/04/01 20:22:10)


ありがとうございます。
参考にさせて頂き、色々と自分で考えて見ます。

># 流石に半月たたないとレスがつかないと、やる気なくなるなぁ、、、

申し訳ありません。
いろいろと考えていました。
しばらく自分で考えて見ます。
とても勉強になっておりました。
申し訳ありませんでした。
一旦閉じさせて頂きます。



この投稿にコメントする

削除パスワード

No.26288

Re:配列
投稿者---かずま(2006/02/27 12:03:47)


参考になりますか?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BYTE_2  8

int main(void)
{
    FILE *fin, *fout;  char buf[1024], *bp, *sp;  long addr;  int n;

    fin = fopen("b.txt", "r");
    if (!fin) fprintf(stderr, "can't open b.txt\n"), exit(1);
    fout = fopen("addr.txt", "w");
    if (!fout) fprintf(stderr, "can't create addr.txt\n"), exit(1);

    while (fgets(buf, sizeof buf, fin))
        for (bp = buf; sp = strstr(bp, "0x"); bp = sp + n + 2) {
            n = 0;
            if (sscanf(sp+2, "%8lx%n", &addr, &n) != 1) break;
            if (n != BYTE_2) continue;
            if (sp >= buf+6 && !memcmp(sp-6, "TOTL :", 6)) continue;
            if (sp >= buf+5 && !memcmp(sp-5, "MAX :",  5)) continue;
            printf("addr = %lx\n", addr);
            fprintf(fout, "%lx\n", addr);
        }
    fclose(fout);
    fclose(fin);
    return 0;
}



この投稿にコメントする

削除パスワード

No.26289

Re:配列
投稿者---かずま(2006/02/27 12:37:13)


>           if (sscanf(sp+2, "%8lx%n", &addr, &n) != 1) break;

break; を continue; に訂正します。



この投稿にコメントする

削除パスワード

No.26291

Re:配列
投稿者---かずま(2006/02/28 03:22:39)


出力に "0x" を付けるのを忘れていました。
#include <stdio.h>
#include <string.h>

int main(void)
{
    FILE *fin, *fout;  char buf[1024], *p;  long addr;  int n;

    fin = fopen("b.txt", "r");
    if (!fin) return fprintf(stderr, "can't open b.txt\n"), 1;
    fout = fopen("addr.txt", "w");
    if (!fout) return fprintf(stderr, "can't create addr.txt\n"), 1;

    while (fgets(buf, sizeof buf, fin))
        for (p = buf; p = strstr(p, "0x"); p += n + 2) {
            n = 0;
            if (sscanf(p + 2, "%8lx%n", &addr, &n) != 1) continue;
            if (n != 8) continue;
            if (p >= buf+6 && !memcmp(p-6, "TOTL :", 6)) continue;
            if (p >= buf+5 && !memcmp(p-5, "MAX :",  5)) continue;
            printf("addr = 0x%08lx\n", addr);
            fprintf(fout, "0x%08lx\n", addr);
        }
    fclose(fout);
    fclose(fin);
    return 0;
}



この投稿にコメントする

削除パスワード

No.26408

Re:配列
投稿者---ぴよ(2006/03/12 23:45:39)


かずまさん

遅くなりまして申し訳ありません。
どうもありがとうございました。
データにTOTL :とMAX : が入っているため、
思っている通りに動きました。

> if (!fin) return fprintf(stderr, "can't open b.txt\n"), 1;

                               ↑ ↑
                       このカンマと1は
return とfprintfの括弧が終わった後にありますが、
どういう意味になるのでしょうか?
fprintf関数を調べても分かりませんでした。
申し訳ありませんが、よろしくお願いします。



この投稿にコメントする

削除パスワード

No.26411

Re:配列
投稿者---かずま(2006/03/13 02:42:55)


>                        このカンマと1は
> return とfprintfの括弧が終わった後にありますが、
> どういう意味になるのでしょうか?
   if (!fin) return fprintf(stderr, "can't open b.txt\n"), 1;
   if (!fin) return (fprintf(stderr, "can't open b.txt\n"), 1);
   if (!fin) { fprintf(stderr, "can't open b.txt\n"); return 1; }

全部同じ動作をします。
main からの return 後は exit が呼び出されるので、次も同様です。

   if (!fin) fprintf(stderr, "can't open b.txt\n"), exit(1);
   if (!fin) (fprintf(stderr, "can't open b.txt\n"), exit(1));
   if (!fin) { fprintf(stderr, "can't open b.txt\n"); exit(1); }

> fprintf関数を調べても分かりませんでした。

コンマ演算子を調べてみてください。


この投稿にコメントする

削除パスワード

No.26555

Re:配列
投稿者---ぴよ(2006/04/01 20:23:39)


ありがとうございます。
とても勉強になりました。
もう一度自分で考えて見ます。
あまりにも時間がかかっているため、
申し訳ありません。
一旦閉じさせて頂きます。



この投稿にコメントする

削除パスワード

管理者用メニュー    ツリーに戻る    携帯用URL    ホームページ    ログ    タグ一覧