ファイル入出力で制御文字がある場合は

解決


いさみちゃん  2004-10-08 23:36:41  No: 54677

いつもVBのほうで御世話になっております。

今回は未熟なVBよりさらに不得手なC++について
質問をさせていただこうと思って居ります。

Aファイルを読込み、2行目以降のデータを出力して
Bファイルを作成する、という処理なのですが・・・

Aファイルに制御文字が入っているとうまく出力できません。。

現在のソースの、オープンと書き出しは
以下のようになっています。

char   *chFileBuff = NULL;
#define   RECLEN  1000

fp = fopen( chInFilePath, "r" )
fgets( chFileBuff , RECLEN, fp);

fp2 = fopen( chOutFilePath, "w" )
ntlen=strlen(chFileBuff);
intDataCnt = fwrite(chFileBuff,intlen, 1, fp2);

fget→fread  
r→rb
w→wbにしたり、
データを読み込む"chFileBuff"変数を
ポインタから通常の変数へ変更したりなどいろいろ試したのですが
上手くいきません。

問題点が分かる方がいらっしゃいましたら
宜しくお願い致します。


いさみちゃん  2004-10-08 23:38:10  No: 54678

書き忘れてました。

ポインタは

chFileBuff=(char *) malloc ( RECLEN );

で領域を確保しています。


西園寺  2004-10-09 01:55:42  No: 54679

> Aファイルに制御文字が入っているとうまく出力できません。。
「うまく出力できない」を具体的に説明してください。
Aファイルの内容がどんなで、Bファイルがどうなるのでしょうか?
うまく出力できなくなるときに入っている制御文字は何ですか?
(改行?タブ?NULL文字?それ以外?)

Aファイルはテキストデータですか?バイナリデータですか?
テキストならr/wでファイルを開いてfgets/fprintf、
バイナリならrb/wbでファイルを開いてfread/fwrite、
の組み合わせでやれば、たいがい大丈夫だと思います。


いさみちゃん  2004-10-09 02:39:19  No: 54680

西園寺さん(高貴な御名前ですね)
ありがとうございます。
テンパったあまり、抽象的でしたね…

Aファイルはバイナリデータです。
制御文字は、バイナリで見えるところの"00"です。

>rb/wbでファイルを開いてfread/fwrite
ファイルの一行目には必ず改行がはいっているので、
読み込みはfgetを使っていますが
出力はfwriteを使用しています。
          ↓
"00"が出力時に改行になってしまいます


西園寺  2004-10-09 03:51:32  No: 54681

> intDataCnt = fwrite(chFileBuff,intlen, 1, fp2);
ふと気づいたのですが、第2と第3引数は逆じゃないでしょうか。
size_t fwrite(const void *buf, size_t size, size_t n, FILE *fp); 
sizeバイトのデータをn個書き込む、ってなると思うので。

> "00"が出力時に改行になってしまいます
ん〜、どうしてか分からないですね。
いさみちゃんさんはどんなコードを書いていますか?

ためしに、こんな感じで書いてみました。
1行目が改行で終わっていて、2行目以降が'\0'を含むバイナリデータの
ファイルを読み込ませたけど、改行に化けたりはしませんでした。

#include <stdio.h>
#define   RECLEN  1000

int main(void)
{
    FILE *fp1, *fp2;
    char chFileBuff[RECLEN];
    int intlen;

    fp1 = fopen("A.dat", "r");
    fp2 = fopen("B.dat", "w");

    // 最初の1行を読む
    fgets(chFileBuff, RECLEN, fp1);

    // 2行目以降はバイナリとして扱う
    while (intlen = fread(chFileBuff, 1, RECLEN, fp1) ) {
        fwrite(chFileBuff, 1, intlen, fp2);
    }

    fclose(fp1);
    fclose(fp2);
    return 0;
}


いさみちゃん  2004-10-14 01:53:01  No: 54682

ちょっと間が開いてしまいました・・・
わたしが記述したコードは

    //処理データオープン
   if ( ( fp = fopen( chInFilePath, "rb" ) ) == NULL ) {

        RsscErrWrite("", 1, &gt_Errinfo);
        return NG;
    }

    // 転送データ抽出
    lng_reccnt = 0;
    for ( int_Cnt = 0 ; !feof( fp )  ;  int_Cnt++ )
    {
    lng_reccnt++;
    memset( chFileBuff, NULL , sizeof(chFileBuff) );
    fgets( chFileBuff , RECLEN, fp);

    //ファイル一行目に転送ファイル名が入っている
    if (lng_reccnt==1){
      //末尾の改行コードを除く
      memset( chOutFileName, NULL ,                                                 sizeof(chOutFileName) );
      intlen=strlen(chFileBuff);
      strncpy(chOutFileName,chFileBuff,(intlen-2));

      //転送ファイル作成
      memset( chOutFilePath, NULL ,                                                 sizeof(chOutFilePath) );
      strcpy( chOutFilePath,  pchOutFilePath  );
      strcat( chOutFilePath,  chOutFileName   );

      if ( ( fp2 = fopen( chOutFilePath, "wb" ) ) ==                               NULL ) {
        
    //二行目以降は転送ファイルデータ
    }else{

      intlen=strlen(chFileBuff);
                        intDataCnt = fwrite(chFileBuff, intlen, 1,                                                 fp2);

       if ( intDataCnt== 0 ) {
        
        RsscErrWrite("", 1, &gt_Errinfo);
        return NG;  
       }

    }
      if ( feof( fp ) ) {
            break;
        }

  }
  // 委託データクローズ
        fclose(fp); 
  fclose(fp2);

こんな感じです。


Ban  2004-10-14 03:18:37  No: 54683

>   fgets( chFileBuff , RECLEN, fp);

fgets の s は string、テキスト用の関数です。
多分、この時点でおかしくなってるんじゃないでしょうか。
fread を使ってみてください。


RAPT  2004-10-14 11:36:19  No: 54684

fgets()は、'\0'か改行文字までを読み込み、バッファに余裕があれば最後に
改行文字を付加します。

バッファ全部を使い切った場合、改行文字は付加されません。

この辺は、標準関数なので、関数仕様を確認してください。


YuO  2004-10-14 18:08:30  No: 54685

バイナリを扱うときにstrlenを使うことはできません。
この時点で,多分データが消えているのでしょう。

ついでに無駄なmemsetが多い気がします。
さらに,memsetの第二引数にNULLを指定するのは誤りです。
第二引数はintであってポインタではないですから。
http://www.kouno.jp/home/c_faq/c5.html#9
http://www.cmagazine.jp/src/kinjite/c/null.html#index4

> fgets()は、'\0'か改行文字までを読み込み、バッファに余裕があれば最後に
> 改行文字を付加します。
> バッファ全部を使い切った場合、改行文字は付加されません

fgetsはナル文字があろうが全く無視して読み続け,バッファが埋まるか,
改行文字を見つけるか,ファイルの末尾まで達したときに,読み込みを終了します。
改行文字を見つけた場合はそのまま改行文字をバッファに書き込みます。
そして,バッファにナル文字を追加します。

fgetsが付加するのは最後のナル文字のみであり,
改行文字を勝手に付加することはありません。


いさみちゃん  2004-10-14 19:25:47  No: 54686

Banさん、RAPTさん、YuOさん、ありがとうございます。

>fgets の s は string、テキスト用の関数です。
まず、fgetsを使用しているのは、
一行目のデータは改行までを取得したいからです。
そこで・・・
二行目以降はfreadを使用するようプログラムを組んだのですが
上手くいきませんでした。
この場合、全てのレコードをfreadで取得し、
一行目だけは改行までを1レコードとするような
処理のほうがよいのでしょうか?

>さらに,memsetの第二引数にNULLを指定するのは誤りです。
>第二引数はintであってポインタではないですから。
そうだったのですか?!このプログラムは会社の既存のものを
流用しているのですが・・・
わたしの会社にあるCのプログラムはほぼ全部この形式です・・・

>バイナリを扱うときにstrlenを使うことはできません。
これはString型の時だけということですね。
(Str…ですもんね)
バイナリの時はどうするか、調べてみたいと思います。


西園寺  2004-10-14 19:42:10  No: 54687

> 二行目以降はfreadを使用するようプログラムを組んだのですが
> 上手くいきませんでした。

どのように上手くいかなかったのでしょうか?
先日、1行目をfgetsで、2行目以降をfreadで読み込むサンプルコードを書きましたが、
あれは役に立ちませんでしたか?

> わたしの会社にあるCのプログラムはほぼ全部この形式です・・・

その会社の「文化」なのでしょうが、ダメな点を真似することはありません。
社内だけで勉強せず、世の中の良書を読み漁って正しい知識を得ましょう。
#アホな先輩はどんどん追い抜いてしまえ!

> これはString型の時だけということですね。
> (Str…ですもんね)
> バイナリの時はどうするか、調べてみたいと思います。

正確に言うと、CにString型という型はありません。
char型の配列でデータの終端に'\0'があるものを文字列として扱っているだけです。

バイナリデータをfreadで読み込めば、戻り値で読み込んだバイト数が得られます。
詳しくはfreadの関数仕様を参照してください。


いさみちゃん  2004-10-15 04:24:57  No: 54688

西園寺さんに頂いたものを元に
このように組んでみました

  //inputファイル名&ファイルパス連結
  strcpy( chInFilePath,  pchInFilePath  );
  strcat( chInFilePath,  gt_initKeyJoho.filename  );

    fp = fopen(chInFilePath, "r");

    // 最初の1行を読む
    fgets(chFileBuff, RECLEN, fp);
  memset( chOutFilePath, NULL , sizeof(chOutFilePath) );
  strcpy( chOutFilePath,  pchOutFilePath  );

  memset( chOutFileName, NULL , sizeof(chOutFileName) );
  strncpy(chOutFileName,chFileBuff,(13-1));←ファイル名となるので
                                                   後のNULLを削除するため
  strcat( chOutFilePath,  chOutFileName   );

    fp2 = fopen(chOutFilePath, "w");

    // 2行目以降はバイナリとして扱う
    while (intlen = fread(chFileBuff, 1, RECLEN, fp) ) {
        fwrite(chFileBuff, 1, intlen, fp2);
    }

    fclose(fp);
    fclose(fp2);
    return 0;

}

これだと、fwriteのところで「ハンドルされていない例外は…」
になってしまいました。
> "00"が出力時に改行になってしまいます
上記データに関しては。

しかしながら、元々正常に処理されていたファイルでは
成功しました。
元データに何か原因があるのでしょうか・・・


いさみちゃん  2004-10-15 04:27:27  No: 54689

追加で

上記の
>strncpy(chOutFileName,chFileBuff,(13-1));←ファイル名となるので
>                                          後のNULLを削除するため

は、一行目のデータレングスが12バイトだからです。
本データは可変なのですが、strlenが
怪しそうだったのであえてこうしてみました。


西園寺  2004-10-15 18:52:25  No: 54690

> これだと、fwriteのところで「ハンドルされていない例外は…」
> になってしまいました。

問題となるデータファイルの中身が分からないので何とも言えませんが、
正常に処理されるファイルとの違いは何でしょうか?

デバッガでステップ実行しながら、freadでchFileBuffやintlenに
どんな値が読み込まれるのかを確認してください。
freadの段階で00→改行に化けるのかどうか、など。

>strncpy(chOutFileName,chFileBuff,(13-1));←ファイル名となるので
>                                          後のNULLを削除するため

strlenは別に怪しくないと思います。
fgetsで読み込んだ1行目には'\0'は含まれていないですよね?
それに、strncpyは文字数を指定しても'\0'以降はコピーしませんから、
同じことです。


いさみちゃん  2004-10-18 18:29:54  No: 54691

皆様、西園寺様ありがとうございます。
このように組んでみたら上手くいきました

  //inputファイル名&ファイルパス連結
  strcpy( chInFilePath,  pchInFilePath  );
  strcat( chInFilePath,  gt_initKeyJoho.filename  );

    //処理データオープン
   if ( ( fp = fopen( chInFilePath, "rb" ) ) == NULL ) {

        return NG;
    }

    // 転送データ抽出
    lng_reccnt = 0;
    for ( int_Cnt = 0 ; !feof( fp )  ;  int_Cnt++ )
    {
    lng_reccnt++;
    memset( chFileBuff, NULL , sizeof(chFileBuff) );
    

    //ファイル一行目に転送ファイル名が入っている
    if (lng_reccnt==1){
      //末尾の改行コードを除く
      fgets( chFileBuff , RECLEN, fp);
      memset( chOutFileName, NULL , sizeof(chOutFileName) );
      intlen=strlen(chFileBuff);
      strncpy(chOutFileName,chFileBuff,(intlen-2));
      strcpy( chRirekiName,  chOutFileName  );
      

      //転送ファイル作成
      memset( chOutFilePath, NULL , sizeof(chOutFilePath) );
      strcpy( chOutFilePath,  pchOutFilePath  );
      strcat( chOutFilePath,  chOutFileName   );

      if ( ( fp2 = fopen( chOutFilePath, "wb" ) ) == NULL ) {
        
        return NG;
      }
    
    //二行目以降は転送ファイルデータ
    }else{
      intlen = fread(chFileBuff, 1, RECLEN, fp);
      intDataCnt = fwrite(chFileBuff, 1, intlen, fp2);
       if ( intlen== 0 ) {
        
        return NG;  
       }

    }
      if ( feof( fp ) ) {
            break;
        }

  }
  // 委託データクローズ
    fclose(fp); 
  fclose(fp2);

何が原因だったのでしょうか??

>正常に処理されるファイルとの違いは何でしょうか?
これについては、このファイルの特徴は
・汎用機用データ(EBCDIC)
・"00"がはいっている
と言ったところでしょうか・・・
その他のEBCDICデータは元々上手くいっていたのですが、
たまたまだったのかもしれませんね。

長らく御付き合いいただき、
処理としては解決することが出来ました。
原因ははっきりわかりませんが…
ありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加