🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: fj.lang.c
Article #626

Re: [Q]区切りなしデータを文字型でない配列に読むには?

#626
From: Tomoaki Chiba
Date: Mon, 23 Mar 1998 00:00
259 lines
12917 bytes

千葉@東北大です。

近々引っ越すので、自宅で荷作り&引越し屋さんと打ち合せしており、
フォローが送れました。しかも昨日はうちの建物が停電だったらしい
のでもしメールを送って頂いた方がいらしたら届かなくて済みません。

その間、いろいろな方法を提案して頂けてありがたく思います。
(mineshitaさん、takeuchiさん、shinoさん、voidさん、masao.seki
  さん、kounoikeさん、makoさん、どうもありがとうございました。)


皆さん御指摘の通り、データや条件の説明としてはっきりしない部分
が多数ありましたので、以下にフォローする形で明示します。

-------------------------------------------------------------------

fj.lang.cの<6esmcd$30l$1@sunny.njk.co.jp>の記事において
JST時間1998年03月20日(金)12時04分45秒頃、minesita@rd.njk.co.jpさんは
書きました。

  |  根本的にはデータ構造が悪いのでそれを考え直すのが一番です。
  | #身も蓋もありませんが……(^^;

自分もその通りだと思います。実は元データは

建設省国土地理院       発行・刊行
(財)日本地図センター 発売
数値地図メッシュ(標高)

のものです。内容はその名のごとく日本にメッシュ状に縦横線を引いて
分割してその各メッシュにおける標高の代表値をCD-ROMに格納し
たものです。50m,250m,1kmそれぞれの間隔で区切ったものがあります。
詳しくは
http://www.jmc.or.jp/suchiindx.html
の該当部分にあります。
データの格納形式はファイル仕様という項目に書いてあります。
http://www.jmc.or.jp/250m/doc/info250.htm
などです。

各ファイルには250m,1kmメッシュでは国土地理院の形式である1次メッ
シュ(緯度1/3度、経度1度)の範囲、50mメッシュでは同2次メッシュ
(1次メッシュの1/64の面積すなわち1/8の辺長)の範囲のデータが入
っています。
実際に計算してみるとわかりますが、250mメッシュでは320x320、1km
では80x80、50mでは200x200のデータがそれぞれのファイルに入ってい
ます。
各ファイルはそのメッシュのデータがいろいろ入っているファイルヘッ
ダ(標高データだけ読む時は飛ばす)がまずあって、その後に標高デー
タが「緯度毎に一行ずつ」格納されています。(ファイルフォーマット
およびレコード構成のところを参照して下さい。)
ただし各行の先頭にはメッシュコードとレコード番号なるものが入れて
あって、各行の最後は「復帰・改行(CR・LF)」です。(レコード
フォーマットを参照して下さい。)

# ちなみにこのデータを扱う人は、いったんスペース区切りを入れた
# 形式にデータを変換して別に保存している場合が多いようです。
# 一応、毎回計算の度にCD-ROMにアクセスする必要があるなど
# という使い方はしておりません。
# ただ自分は全国のデータをハードディスクに保存しようとして、
# "file sytem full"を起こしたことがありましたが...


  | > なぜこんなことを考えているかというと、最初は
  | > 
  | > int i;
  | > FILE *fp;  /* データファイルのファイルポインタ */
  | > char CharArray[6]; /* 5バイト分?のデータを一時的に読み込む配列 */
  | > double DblData[350];  /* 実際にデータを格納する配列 */
  | > for(i=0; i<350; i++) {
  | >   fread(CharArray, 5, 1, fp);
  | >   CharArray[6] = '\0';
  | >   DblData[i] = atof(CharArray);
  | > }
  | 
  |  概ねこういう形になるでしょう。
  |  多分、他を試してもこれ以上は簡単にならなかったんじゃないで
  | しょうか?

# これより簡単な方法はやはりないのでしょうか?


  | > ここで、ループを使って5文字分ずつ読んでいくのではなく、一行分ずつ
  | > 一気に読んでしまいたいという考えが浮かんできました。そこでまずは
  | > 
  | > char CharArray2D[350][5];
  | > fread(CharArray2D, 5, 350, fp);
  | > for(i=0; i<350; i++)
  | >   DblData[i] = atof(CharArray2D[i]);
  | 
  |  ループを使っていますよね?

説明不足ですみません。この段階ではとりあえず関数freadで一行の
データをまず丸ごと読んでしまうことを考えていました。しかしこれ
では文字型配列に読み込んだデータを切り出してintやdoubleといっ
た配列に読み込まなくてはいけないのです。つまり問題の形式のデー
タを「freadで文字型配列に読み込むがごとく」(これだとループも
いらず表現も短い一行で済んでしまう、後述の「ダイレクト」とはこ
れを表したくて使いました)、他の型の配列に読む方法はないものか
と思ったのです。「簡潔」というのにできればこの「ダイレクト」も
加えて頂くとありがたいです。


  | > そこで次にダイレクトに
  | > FILE *fp;  /* データファイルのファイルポインタ */
  | > double DblData[350];  /* 実際にデータを格納する配列 */
  | > fread(DblData, 5, 350, fp);
  | > 
  | > などということはできないかと思ってやってみました(というかこれで
  | > すんなりうまくいっていたらニュースには投稿していませんでしたが)。
  | 
  |  何故出来ると考えられるかが問題な気がします。(^^;
  |  根本的に変数がメモリにどういう風に存在するのかを理解する
  | べきだと思います。

「ダイレクト」を実行すべく、このようにして読めれば儲け物と思ってやって
みましたが、やはりだめでした。
freadがどういうデータのやりとりをしているのか探るべく、stdio.h(自分の
環境では /usr/include にある)を覗いてみましたが、「C言語で書いてある
ようだ」ぐらいのことしかわからず挫折しました。ちなみに grep fread を他
のいろいろなところにもかけてみましたが、このファイル内にしか見つかりま
せんでした。
メモリへのデータの格納の様子は良く知らないのですが、fread のオンライン
日本語マニュアルにある「バイト・シーケンス」というのは、データを収める
変数のsizeであって、読み込むファイルをasciiコードなら1文字1バイトな
どと考えるのでは決してないということですね(どうも初心者には無理矢理そ
のように解釈してしまうこともできるようなマニュアルの説明だと思うのです
が)。
とすると、逆にたとえばもし以前自分がエラーしたようにdoubleなら0.0より
非常に小さい値(値による?)、intなら大きな整数値としてテキストには表
示される、それらの型に「ダイレクト」に読み込めるデータファイルのような
ものを作れるということでしょうか?(それもいわゆるバイナリーファイル?)

-----------------------------------------------------------------------

fj.lang.cの<1998Mar20.035036.3299@merope.opus.or.jp>の記事において
JST時間1998年03月20日(金)12時50分36秒頃、void@merope.opus.or.jpさんは
書きました。

  | fj.testは練習用のNewsGroupではないですよ。

失礼しました。(注・本題とは関係ないです)
fj.1st-readme での説明によると Distribution:local 
でテストしろとありました。

# 昔ネットワークニュースの存在を初めて知ったころ
# fj.bigginers? とかいうところに挨拶を兼ねてそう
# いう投稿があった気がします。その頃すでに廃止の
# 議論があったような気がしたのですが、本当になく
# なってしまったのでしょうか?
# (ROM以下のぶんざいですみません)


  | : データの各行は 200~400×5 個位の数字から成り立っているということです。
  | : すなわち行ごとに例えば Data[350] のような配列に読み込むことを考えます。
  | 
  | どうして200~400なのに「350」なんていう宣言をするのでしょうか?

全く説明なしでした。冒頭に示した通り扱うメッシュによって配列の大きさは
事前にわかります。


  | : DblData[i] = 10000 * ( CharArray2D[i][0] - '0' ) \
  | :               + 1000 * ( CharArray2D[i][1] - '0' ) \
  | :               +  100 * ( CharArray2D[i][2] - '0' ) \
  | :               +   10 * ( CharArray2D[i][3] - '0' ) \
  | :               +    1 * ( CharArray2D[i][4] - '0' );
  | : のようにすれば(足し引き間違ってたらすみません)、atof系の関数は使わな
  | : くてもよいのですが、これはとりあえず「可能な限り簡潔」ではないとさせて
  | : 下さい。
  | 
  | うーむそれなりに簡潔だとおもいますがね。
  | 5回分をループにすれば2行で書けますし。

おっしゃる通りでした。すこぶる簡潔な表現で埋め込めますね。
#include <math.h>
for(j=0; j<5; j++)
        DblData[i] = power(10,4-j) * (CharArray2D[i][j] - '0');
といったところでしょうか?


  | : そこで次にダイレクトに
  | : FILE *fp;  /* データファイルのファイルポインタ */
  | : double DblData[350];  /* 実際にデータを格納する配列 */
  | : fread(DblData, 5, 350, fp);
  | : などということはできないかと思って
  | 
  | なぜ「double」なんていうものが出てくるのでしょう?
  | しかもそれが「ダイレクトに」っていうのが意味不明です。
  | そもそも、そのあたりの理解不足が原因のようですね。

後で読み込んだデータに対して計算を行なうので、例えばここでは
doubleに読んでいますが、intやlong intやfloatでも構いません。
文字配列に格納した数字に対して計算をdouble型などで行なうごと
く簡潔にできるような方法を自分は知りません。(たぶんそういう
意味ではなくデータのメモリへの格納云々のことをおっしゃってい
るのだと思いますが...)


  | どうしても「scanf」系の関数が使いたい。それを「簡潔」だと思っている

scanfにはこだわりません。ただ、区切り文字がない時に5文字と
フィールド幅が決まっている時には、フィールド幅指定を使って
「ループを使わず、一行で文字型以外の配列に」読み込むのには
適していると思いました。配列が大きいととてつもなく長くなっ
てしまいますが。


  | 話の内容からintは16bitではない環境だと予想されますが、
  | 16bit程度のintだと、5桁の10進数はintに納まらないので注意が必要ですね。

sizeofで調べたところ、int,long int,float が 4byte=32bit、
doubleが 8byte でした。無論 charは 1byte です。(昔この
辺のネタで盛り上がってたようですけど突っ込まないで下さい)

------------------------------------------------------------------------

fj.lang.cの<1998Mar20.165851.12872@merope.opus.or.jp>の記事において
JST時間1998年03月21日(土)01時58分51秒頃、void@merope.opus.or.jpさんは
書きました。

  | Masahiro Kondo (mako@mail.raidway.ne.jp) wrote:
  | : 「長くて区切り文字のない文字列」って?
  | : 区切り文字の定義があいまいですが・・・(区切りが無ければ行って観念もない
  | : ですね?)
  | 
  | あ、わたしは「行」の区切りはあって、
  | 行の中の区切りがないものだと思って、それを読むコードを例示してしまったけど
  | そういう意味ではなかったのか...また失敗...。

これも全く説明不足で失礼しました。冒頭で説明しましたが、
voidさんのおっしゃる通り「行の中の区切りがない」という
ことです。それと文字列ではなくて「ファイル中にテキスト
形式?(char型のバイト・シーケンス?)で表された数字の
列」とでもいえばいいのでしょうか?
あと参考までに区切りがないというのは正確ではなくて、実
際には各データの末尾は必ず '0'(ゼロ)になっています。
しかし各データの頭に出てきた時の'0'と見分けがつかず、
のちのち標高測定の精度が上がった時にこのフィールドが使
われる可能性もあるため、これを区切りとして利用すること
は勧められないと思います。

-------------------------------------------------------

また長くなってしまいましたが、こんなところです。
「素直にスペース区切りを入れたデータに変換するプログラ
ムを組め!」という御意見があるでしょうが、「ダイレクト」
に(できれば初心者にわかりやすく)読む方法があるかどうか
という興味からのみ投稿しております。
もっとも簡潔な方法(短い表現であることを含む)というのは
今まで出た以外にはやはりありえないのでしょうか?

---------------------------------------------------------------
Name       : Tomoaki Chiba 千葉 智章
           : Inst.Geol.Paleon.Dep.Geo.Env.Sci.Tohoku.Univ
           : 東北大学理学部地圏環境科学科地質学古生物学教室4年
E-mail     : chiba@dges.tohoku.ac.jp
FAX        : 022-217-6634
Technical Details
Message-ID: <6f67bp$djs@sakunami.gw.tohoku.ac.jp>
Path: arc1-ca.pugleaf.net!unknown!not-for-mail
References: <6esjd9$4t@sakunami.gw.tohoku.ac.jp> <6esmcd$30l$1@sunny.njk.co.jp>

Show Full Headers
Complete Article Headers
X-Google-Language: Japanese,JIS
X-Google-Thread: 106434,8756f6f59b5f78c5
X-Google-Attributes: gid106434,public
From: chiba@dges.tohoku.ac.jp(Tomoaki Chiba)
Subject: Re: [Q]区切りなしデータを文字型でない配列に読むには?
Date: 1998/03/23
Message-ID: <6f67bp$djs@sakunami.gw.tohoku.ac.jp>
X-Deja-AN: 336887095
Distribution: fj
References: <6esjd9$4t@sakunami.gw.tohoku.ac.jp> <6esmcd$30l$1@sunny.njk.co.jp>
Content-Type: text/plain; charset=ISO-2022-JP
Organization: Tohoku University, 980-77 Sendai JAPAN
Mime-Version: 1.0
Newsgroups: fj.lang.c