
C言語で可変長から固定長に変換方法は??
ある可変長のファイルがあって、レコード長を全て40にそろいたいと思って以下のようにプログラム作りました:
・・・・・(省略)
len = strlen(buf); // fscanf使ってファイルから読みみ込んだものbufに格納
n = 40-len;
char * str = (char *)malloc(n+1);
memset(str, ' ', n);
strcat(buf,str); //レコード40バイトになるまで空白を詰めていく
fprintf(fpt,buf);//fprintf使って新しいファイルに書き込み
・・・・・・(省略)
実行してみたところ、全部40という長さになっていない、40超えるものも多数出ました。
これは何がだめですか?fprintfをつかったからだめですか?それともmalloc freeにする必要ありますか?まったく検討つかないです。どうかよろしくお願いします
No.9ベストアンサー
- 回答日時:
>while(fgets(buf,REC,fp)!= NULL){
>len = strlen(buf);
>printf("レコード長 :%d\n",len);
>}
>結果は
>レコード長 :39
>レコード長 :2
よくあるミスです。
fgetsの2番目の引数は「文字列の終端文字であるヌル文字を格納する分を含んだ、バッファの実サイズ」を指定する事になっています。
そして、困った事に「改行文字は、文字列の文字数に含み、改行文字も読み込む」のです。
では、質問者さんのプログラムを検証してみましょう。
while(fgets(buf,REC,fp)!= NULL){
RECは40です。そのため、fgetsは「bufが一杯になる39文字までを読んで、読み込みを打ち切り、末尾にヌル文字を付加」します。
len = strlen(buf);
bufには「39文字+終端のヌル文字」が入っているので、strlenは終端のヌル文字の手前にある文字数の「39」を返し、lenは39になります。
printf("レコード長 :%d\n",len);
printfは「レコード長 :39+改行」を表示します。
while(fgets(buf,REC,fp)!= NULL){
RECは40です。そのため、fgetsは「さっき読み残した1文字と、改行文字を読んで打ち切り、末尾、つまり改行文字の後にヌル文字を付加」します。
len = strlen(buf);
bufには「読み残した1文字+改行文字1文字+終端のヌル文字」が入っているので、strlenは終端のヌル文字の手前にある文字数の「2」を返し、lenは2になります。
printf("レコード長 :%d\n",len);
printfは「レコード長 :2+改行」を表示します。
結果、質問者さんのプログラムは
レコード長 :39
レコード長 :2
を表示する事になります。
これは「意図した結果ではないが、fgets()関数の仕様通りの動作」であり「正常に動作している」のです。
単に「意図通りの引数を指定しなかった為、意図した動きをしなかっただけ」なのですね。
そういう訳で「40文字+改行1文字+終端ヌル1文字」で、バッファは42バイト必要で、fgetsには「42文字あるバッファ」と「42」を指定しなければならないのです。
レコード長の定義として「#define REC 40」と定義しているのなら、以下のように書かねばなりません。
#define REC 40
char buf[REC + 2] /* 改行文字と終端ヌル文字も格納できるように、レコード長より2文字分大きいバッファを用意しなければならない */
(途中省略)
while(fgets(buf,siziof(buf),fp)!= NULL){ /* fgetsの第2引数に定数は指定しない事。必ず「sizeof(指定したバッファ)を指定すること */
len = strlen(buf);
if (buf[len - 1] == '\n') buf[--len] = '\0'; /* 末尾に改行があるなら、末尾の改行を終端文字ヌルで上書きして取り去り、その分、lenを1減らす */
printf("レコード長 :%d\n",len);
}
No.8
- 回答日時:
追記な訂正。
>fprintf(fpt,40,"%-40s\n",buf);
は
fprintf(fpt,"%-40s\n",buf);
の誤り。度々すいません。
ありがとうございました。私がやりたいこと的中に当たりました。早速アドバイス通りにプログラム実行してみたのです。作られたファイルをレコード長の長さを確認するため以下のプログラムを作りました。 while(fgets(buf,REC,fp)!= NULL){
len = strlen(buf);
printf("レコード長 :%d\n",len);
}
結果は
レコード長 :39
レコード長 :2
というふうになったですが、これはどいう意味か、教えて頂ければ嬉しいのですが。。。もしかしたら長さ2のところは改行と終端文字の値でしょうか?
ちなみに、C言語を詳しくなるため、お勧めの本があれば教えて頂きたいです。よろしくお願いします
No.7
- 回答日時:
蛇足な追記。
今回のように「40文字の幅に左詰めでファイルに書く」のではなく「40文字以下の、n文字の空白文字の文字列」が欲しいなら
char * str = (char *)malloc(n+1);
memset(str, ' ', n);
str[n] = '\0';
(中略)
free(str);
でも構わないけど、もっと簡単に
char *str;
str = " (ここに空白を40文字並べる:注参照) " + 40 - n;
でOK(但し、strが指す文字列は読み込み専用なので書き換えできない)
注:このサイトで投稿すると「連続した複数の半角空白」は「1つの半角空白」に勝手に直されて投稿されるので40個の空白を並べて書けません。実際にプログラムに書く時は「40個の空白を並べて」書いてください。
No.6
- 回答日時:
>これは何がだめですか?
strにn文字分の' 'を埋め込むまでは良いが、そのstrに終端文字の'\0'を付けてないのがダメ。
厳しい事を言えば、fscanfを使うのもダメだし、memsetを使うのもダメだし、mallocを使うのもダメだし、mallocがエラーを返してくるのをチェックしてないのもダメだし、ちゃんとfreeしているか明記してないのもダメ。
で、どうして失敗しているのかと言うと、例えば
1行目が30文字
2行目が20文字
3行目が35文字
だったとしよう。
1行目は、nが40-30で10、+1して11文字のバッファがmallocで返される。
そこに10文字の' 'を埋める。
運良く、mallocが返したメモリは「最初は全部ゼロで埋まってる」ので、strには「10文字の空白+終端文字を意味する運良く最初からあったゼロ」が出来る。
それを「30文字のbufにstrcatする」ので「30文字+10文字」になる。
そして、使い終わったstrはfree(str);で開放される(けど、そのメモリには10文字の空白が残ってる)
一見、1行目は正しく動く。
2行目は、nが40-20で20、+1して21文字のバッファがmallocで返される。
そこに20文字の' 'を埋める。
運良く、mallocが返したメモリは、さっき使ったメモリと同じ場所なので「10文字の空白と、11文字目以降全部ゼロで埋まってる」ので、strには「20文字の空白+終端文字を意味する運良く最初からあったゼロ」が出来る。
それを「20文字のbufにstrcatする」ので「20文字+20文字」になる。
そして、使い終わったstrはfree(str);で開放される(けど、そのメモリには20文字の空白が残ってる)
一見、2行目も正しく動く。
3行目は、nが40-35で5、+1して6文字のバッファがmallocで返される。
そこに5文字の' 'を埋める。が、そこには「前回mallocして、使用後にfreeした、使い終わったメモリの残骸」があるので、その残骸には「既に20文字の空白が埋まっている」ので、5文字の' 'を埋めても意味は無い。
残念ながら、mallocが返したメモリは、さっき使ったメモリと同じ場所なので「20文字の空白と、21文字目以降全部ゼロで埋まってる」ので、strには「20文字の空白+終端文字を意味する運良く最初からあったゼロ」が出来る。空白を5文字しか埋めてないのに。
それを「35文字のbufにstrcatする」ので「35文字+20文字」になる。5文字の空白を足すつもりで、終端文字を付け忘れた所為で、20文字の空白が足されちゃう訳だ。
そして、使い終わったstrはfree(str);で開放される(けど、そのメモリには20文字の空白が残ってる)
そして、質問者さんは
>全部40という長さになっていない、40超えるものも多数出ました。
って悩む事になる。
とりあえずの修正なら
memset(str, ' ', n);
の直後に
str[n] = '\0';
ってのを追加すれば大丈夫。
で、mallocとかstrlenとかmemsetとか使わず、最も簡単に済ます方法は、以下の通り。
char buf[260] /* scanfを行うのに充分な大きさを確保しておく。もちろん、buf[41]ではダメ */
・・・・・(省略)
buf[40] = '\0'; /* 元々のbufが40文字以上だった時に41文字目以降を捨てる。これを忘れると41文字以上の行が出来てしまう */
fprintf(fpt,40,"%-40s\n",buf); /* 左揃えで40文字の幅に書き込めば終わり */
で、本当の所を言うと「buf[40] = '\0';の処理で、行末で40バイトに切り捨てる際に、漢字などの2バイト文字の後半バイトだけ切り捨ててしまう」と言う事が起きないように「レコードの末尾に2バイト文字の先頭バイトだけ残る場合は、それを空白に置き換える」と言う処理も必要だったりする。
以下蛇足。
冒頭の「fscanfを使うのもダメ」な理由。
もし「入力ファイル名の指定を間違えて、読みこんだファイルがテキストファイルじゃなく、空白文字や改行コードが出てこないバイナリファイルだった」としたら、scanfは何文字のデータをbufに入れようとするだろう?
「うっかり、5メガバイトある動画ファイル」を入力に指定したら?そして、その5メガバイトの中に「空白文字や改行コードが1つも無い」としたら?
scanfは、言われた通り、bufに「空白文字や改行コードが現れるまで読みこもう」とするだろう。
そして「bufのアドレスに5メガバイトのデータを読み込もう」として、プログラムが落ちてしまう。メモリをグチャグチャに壊したままで。
なので「絶対の保障」が無い限り、scanfやfscanfは使っちゃいけない。
No.4
- 回答日時:
#include <stdio.h>
#include <string.h>
#define BUFSIZE 10000
#define WIDE 40
int main(int argc, char *argv[])
{
FILE *fp;
char buf[BUFSIZE];
int n;
if (argc == 1) {
fprintf(stderr, "USAGE: %s <input>\n", argv[0]);
return 1;
}
if (NULL == (fp = fopen(argv[1], "r"))) {
perror("fopen error");
return 1;
}
while (NULL != fgets(buf, BUFSIZE, fp)) {
while (buf[strlen(buf)-1] == '\n' || buf[strlen(buf)-1] == '\r') {
buf[strlen(buf)-1] = '\0';
}
for (n = strlen(buf); n < WIDE; n++) {
buf[n] = ' ';
}
buf[WIDE] = '\n';
buf[WIDE+1] = '\0';
fprintf(stdout, "%s", buf);
}
return 0;
}
ポイントは文字列の長さと改行コード'\n'と終端文字'\0'
No.3
- 回答日時:
質問の内容も、何をやりたいのかも、何故やりたいのかも、何がわからないのかも、
伝わってきません。
ひとつひとつ整理していってください。
●目的。
●理由。
●わかっている事。
●わからない事。
●期待する動作。
●現状どうなっているのか。
> 可変長から固定長
可変長、固定長と書かれていますが、何を指していますか?
可変長、固定長がどい言うものか理解していますか?
> ある可変長のファイルがあって、
これは、何かのデータを格納してあるテキストファイルか何かですか?
> レコード長を全て40にそろいたい
どう言うファイルレイアウトを想像していますか?
ファイル仕様書にはどう定義されていますか?
> 実行してみたところ、全部40という長さになっていない、40超えるものも多数出ました。
どのようにして確認しましたか?
> fprintfをつかったからだめですか?
ファイルの書式によります。
ファイル仕様書にしたがってください。
> それともmalloc freeにする必要ありますか?
判断にあたいするだけの情報が提供されていないようです。
これでは、回答できません。
> まったく検討つかないです。
状況を整理する事からはじめてください。
わかっている事とわかっていない事の切り分けなど進めると、
もう少し有効的な質問が書ける様になるかと思います。
●以下憶測で作成したサンプルプログラムです。
----------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char input_filename[] = "input.txt";
char output_filename[] = "output.bin";
FILE *pInputFile = NULL;
FILE *pOutputFile = NULL;
char inputBuffer[1024];
char editBuffer[40];
int editBufferLen = 0;
/*
* ●ファイルを開く
*/
pInputFile = fopen(input_filename, "r");
if (pInputFile == NULL)
{
perror("入力ファイルを開けませんでした.");
exit(1);
}
pOutputFile = fopen(output_filename, "wb");
if (pOutputFile == NULL)
{
perror("出力ファイルを開けませんでした.");
fclose(pInputFile);
exit(1);
}
/*
* ●ファイルの行毎に処理するためのループ処理
* ファイル終端に達するまで、繰り返し処理する。
*/
while (fgets(inputBuffer, sizeof(inputBuffer), pInputFile) != NULL)
{
/*
* ●1行毎の処理
*/
sscanf(inputBuffer, "%s", editBuffer); /* 行末の改行コードを除去 */
editBufferLen = strlen(editBuffer);
memset(editBuffer + editBufferLen, ' ', sizeof(editBuffer) - editBufferLen);
fwrite(editBuffer, 40, 1, pOutputFile);
}
/*
* ●ファイルを閉じる
*/
fclose(pInputFile);
fclose(pOutputFile);
return 0;
}
----------------------------------------------------------------------
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Excel(エクセル) 2つのVBAを一緒にしたら機能しなくなりました(エクセル) 7 2022/06/02 12:41
- Visual Basic(VBA) Excel-VBAでのファイルの開き方 4 2023/02/14 11:01
- C言語・C++・C# 略語の読み方について 2 2023/05/25 12:35
- C言語・C++・C# c言語 プログラムのエラー 1 2023/02/11 20:31
- C言語・C++・C# sprintf()の使い方について 1 2022/08/17 16:16
- Visual Basic(VBA) 複数csvを横に追加していくマクロについて 2 2023/04/25 09:19
- Visual Basic(VBA) フォルダの場所を可変にしたいです(マクロ) 4 2023/05/11 10:00
- Excel(エクセル) エクセル VBA For Next 繰り返しの書き方を教えてください 6 2022/09/01 14:11
- C言語・C++・C# C言語のファイル入力が分かりません 2 2022/05/22 06:35
- C言語・C++・C# プログラミングの授業の課題です 1 2023/01/17 22:15
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
自作の関数を見てください。
-
初歩的なプログラムなんですが...
-
テキストの中から与えられた文...
-
c言語の課題についての質問です。
-
CでCSVファイルを扱う
-
大学のプログラミング課題について
-
C言語の問題教えてください 定...
-
大文字の文字列→小文字の文字列
-
CStringのFindで文字列検索を行...
-
C言語で、文字の出現頻度を求め...
-
str[j++]の意味
-
if文の判定条件('||'と'&&'の使...
-
printfの引数指定でなぜ文字列...
-
sscanfでフォーマットに合って...
-
単語数のカウントについて
-
C言語 空白の行(改行のみ)が...
-
【続】コマンドライン引数を使...
-
文字の入れ替えについて
-
1行の文字列を時間差で表示する...
-
fgetsで拾われる改行文字を削除...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
CStringのFindで文字列検索を行...
-
fgetsで拾われる改行文字を削除...
-
itoaわかりません
-
sscanfとscanfの違いがよくわか...
-
Cで「大文字、小文字の判定」は...
-
C言語のステップ数をカウントす...
-
fgets関数を使用したときの文字...
-
CStringについて
-
ブランクのチェック
-
単語数のカウントについて
-
C言語 空白の行(改行のみ)が...
-
C言語で可変長から固定長に変換...
-
文字列中に含まれる文字の個数...
-
反転した数値を表示させるやり方
-
fgetsとsscanfを使って一行から...
-
C++
-
メニューバーでクリックした個...
-
fgetsの用法について
-
文字列の途中に「0」がある場...
-
かぶった文字を消すプログラム
おすすめ情報