こんにちは! Вавилонская башняのzaliznia.exe
のDBFファイルを開きたいです。
Windows7でDBFファイルを落として、LinuxのLibreOfficeで開くのですが、文字化けしています。
このDBFファイルにはロシア語の格変化を判定するためのデータが収められているはずなので、ロシア語の文字コードで開ければ良いはずなのですがUTF-8
やCyrillicなんとか
の文字コードを選択しても文字化けがなおりません。
開ける前に文字コードを判定する方法はあるのでしょうか。またはPythonを使って中身を見たりデータにアクセスする方法はあるでしょうか。できればLibreOfficeで開いてファイル全体を見たいです。
よろしくお願いしますorz
文字化け画像など
こんな感じで文字化けします。ちなみに、提示されているすべての文字コードを試しましたが、まともに表示してくれたものはありませんでした。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

回答1件
0
ベストアンサー
調べたことを書きます。
ファイルを調べる
まず、zaliznia.dbfの中身を調べることにしました。これはどうやらdBASEのデータベースファイルらしいので、ファイルフォーマットの仕様を探して読みました。たとえばここにあります。
odを使ってファイルの中身をダンプして、仕様とくらべてみました。dBASE IIIの形式のようですが、ところどころ違っています。あと、一緒に配布されているzaliznia.varファイルについては、仕様のどこにも記述がありません。
が、フィールドの定義とレコード長、最初のレコードが格納される位置は仕様に基づいて計算できたので、レコードを読んで出力するスクリプトを書いて実行してみました。
bash
1$ python3 zaliznia-2.py zaliznia.dbf 2'\x01\x00\x00\x00\x03\x00' '\t\x00\x00\x00\x15\x00' ' ' 3'#\x00\x00\x00\x03\x00' '+\x00\x00\x00\x15\x00' ' ' 4'E\x00\x00\x00\x05\x00' 'O\x00\x00\x00\x07\x00' ' ' 5'[\x00\x00\x00\x07\x00' 'g\x00\x00\x00\x15\x00' ' ' 6'Б\x00\x00\x00\x02\x00' 'И\x00\x00\x00\x07\x00' ' ' 7'Ф\x00\x00\x00\x05\x00' 'Ю\x00\x00\x00\x07\x00' ' ' 8'к\x00\x00\x00\x03\x00' '▓\x00\x00\x00\x15\x00' ' ' 9'╠\x00\x00\x00\x01\x00' '╥\x00\x00\x00\x14\x00' 'ы\x00\x00\x00\x0e\x00' 10'■\x00\x00\x00\x01\x00' '\x04\x01\x00\x00\x07\x00' ' ' 11'\x10\x01\x00\x00\x01\x00' '\x16\x01\x00\x00\x07\x00' ' ' 12'"\x01\x00\x00\x01\x00' '(\x01\x00\x00\x1a\x00' ' ' 13'G\x01\x00\x00\x07\x00' 'S\x01\x00\x00\x05\x00' ' ' 14']\x01\x00\x00\x04\x00' 'f\x01\x00\x00\x1c\x00' ' ' 15'З\x01\x00\x00\x03\x00' 'П\x01\x00\x00\x07\x00' ' ' 16'Ы\x01\x00\x00\x06\x00' 'ж\x01\x00\x00\x06\x00' '▒\x01\x00\x00\x10\x00' 17'╞\x01\x00\x00\t\x00' '╘\x01\x00\x00\x07\x00' ' ' 18(以下略)
質問者さんとよく似た文字化けのしかたをしています。つまり、レコードを読めているのは間違いないですが、データを正しく変換できていないようです。
しかし、フィールドの幅が6バイトでは、文法情報などを格納するには短かすぎます。そもそもテキスト型に\x00
などがやたらと入っているのは変です。
これはテキストデータではなく、数値のような固定長データだと思ったほうがよさそうです。そう思ってよく見ると、各フィールドは6バイトのうち、前の4バイトと後の2バイトに分けられるようです。なので、それぞれを整数として出力してみました (後に0x00が詰められることから、リトルエンディアンで格納されていると仮定しました)。
bash
1$ python3 zaliznia-3.py zaliznia.dbf 2[1, 3] [9, 21] None 3[35, 3] [43, 21] None 4[69, 5] [79, 7] None 5[91, 7] [103, 21] None 6[129, 2] [136, 7] None 7[148, 5] [158, 7] None 8[170, 3] [178, 21] None 9[204, 1] [210, 20] [235, 14] 10[254, 1] [260, 7] None 11[272, 1] [278, 7] None 12[290, 1] [296, 26] None 13[327, 7] [339, 5] None 14[349, 4] [358, 28] None 15[391, 3] [399, 7] None 16[411, 6] [422, 6] [433, 16] 17[454, 9] [468, 7] None 18(以下略)
きれいな結果が出ました。それぞれのフィールドのうち前の値は、他のフィールドやレコードの値と重複がなく、はっきりした規則性をもって増加しています。
フィールドごとの値は、まだ見ていないzaliznia.varファイルの中の位置と長さを表していると思われます。これを確認するために、たとえば、上の8番目のレコードの値に従って次を実行してみると、
bash
1$ dd bs=1 skip=204 count=1 if=zaliznia.var 2>/dev/null |iconv -fcp866 -tutf-8 2а 3 4$ dd bs=1 skip=210 count=20 if=zaliznia.var 2>/dev/null |iconv -fcp866 -tutf-8 51 (_без удар._) союз 6 7$ dd bs=1 skip=235 count=14 if=zaliznia.var 2>/dev/null |iconv -fcp866 -tutf-8 8ah and butI eh
これは、オンラインで提供されているデータベースの検索結果と一致します。
Starling databaseについて
ここまで調べたあとで、サイトにあった解説をみつけ、このサイトで使われているのはStarling Databaseというものだと知りました。dBASEを元に拡張されたファイル形式だそうです。
名前が分かったので、GitHubにある資料も見つけることができました。Starlingファイル形式についての解説文書もあったので、ここまで試したことはほぼ間違いないことがわかりました (なお、文字コードはcp866に改変を加えた独自のものを使っているようです)。
Pythonでの方法
上でzaliznia.dbfの内容を出力するために作ったスクリプトを貼っておきます。上の解説を読んで必要な処理を追加すれば、zaliznia.varから実データを読み出すこともできるようになるでしょう。
python
1#-*- python -*- 2 3import sys 4from struct import calcsize, unpack 5 6''' 7最初のレコードのオフセット。 8ファイルによって異なるはずだが、簡単のため決め打ちする。 9オフセットの求めかたはdBASE IIIかStarlingの仕様を参照。 10''' 11initial_offset = 0x82 12''' 13レコードの形式。 14先頭のフラグ1バイトのあとに、カラムの値がパックされて格納される。 15レコードの構成はファイルによって異なるはずだが、簡単のため決め打ちする。 16''' 17record_format = 'B 6s 6s 6s' 18 19def read_records (ioh): 20 ''' 21 IOからレコードを読んで返すジェネレータ関数。 22 ''' 23 record_size = calcsize(record_format) 24 25 ioh.seek(initial_offset) 26 while 1: 27 buf = ioh.read(record_size) 28 if not buf or len(buf) < record_size: 29 break 30 31 flag, word, gram, tran = unpack(record_format, buf) 32 if flag == 0x1A: # 終了 33 break 34 if flag == 0x2a: # 削除されたレコードはとばす 35 continue 36 elif flag != 0x20: 37 raise ValueError('Unknown flag %r' % flag) 38 39 # yield word, gram, tran 40 # yield word.decode('cp866'), gram.decode('cp866'), tran.decode('cp866') 41 yield unpack_ref(word), unpack_ref(gram), unpack_ref(tran) 42 43def unpack_ref(col): 44 ''' 45 VARファイルへの参照値を [オフセット, 長さ] の形式にして返す。 46 Starling仕様参照。 47 ''' 48 if col == b' ': 49 return None 50 else: 51 return list(unpack('<LH', col)) 52 53def main(): 54 ioh = open(sys.argv[1], 'rb') 55 for row in read_records(ioh): 56 # print('%-27r %-27r %r' % row) 57 print('%-16r %-16r %-16r' % row) 58 59if __name__ == '__main__': 60 main()
投稿2018/10/25 05:19
編集2018/10/25 08:50総合スコア4443
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。