VBでのバイナリデータの開き方を教えて下さい。
開きたいファイルは下記にあります。
http://23h2.com/00000001.zip
一部、テキストにしたものはこちらです。
http://23h2.com/sample.txt
gizmo5 さんの回答にあった
■ Binary file format for combat logs - ffxiv
http://www.reddit.com/r/ffxiv/comments/1vey94/binary_file_format_for_combat_logs/
を参考に、読み込むサンプルを書いてみました。
(やっつけなので、バグってるかも知れません。)
Imports System Imports System.IO Imports System.Text Module Module1 ' <参考> ' http://i.imgur.com/by3VaAX.png Sub Main() Dim bytesFile() As Byte = File.ReadAllBytes("00000001.log") Dim bytesBody(3) As Byte ' ヘッダ情報取得用 Dim bytesPos(3) As Byte ' ヘッダ情報取得用 Array.Copy(bytesFile, 0, bytesBody, 0, 4) ' 先頭の4バイトを取得 Dim nLength = BitConverter.ToInt32(bytesBody, 0) ' 先頭の4バイトをレコード数として取得する Dim nPos As Integer ' 現在の位置 Dim nPrevPos As Integer ' 一つ前の位置 Dim nSize As Integer ' 1レコードのサイズ Dim i As Integer nPrevPos = 0 ' レコード数分ループする For i = 0 To nLength - 1 ' レコードの位置を取得 Array.Copy(bytesFile, 8 + i * 4, bytesPos, 0, 4) nPos = BitConverter.ToInt32(bytesPos, 0) ' 1レコードのサイズを算出する(現在の位置-1つ前の位置) nSize = nPos - nPrevPos ' 1レコード取得する Dim bytesRecord(nSize - 1) As Byte Array.Copy(bytesFile, 8 + nLength * 4 + nPrevPos, bytesRecord, 0, nSize) ' データを解析する Parse(bytesRecord) ' 一つ前の位置を変数に保持しておく nPrevPos = nPos Next End Sub ' 解析処理 ' <参考> ' http://www.reddit.com/r/ffxiv/comments/1vey94/binary_file_format_for_combat_logs/ Sub Parse(bytesRecord() As Byte) Dim nLength As Integer = bytesRecord.Length ' 日付情報取得 Dim nTimeStampSize = 14 Dim bytesTimeStamp(nTimeStampSize - 1) As Byte ' タイムスタンプ Array.Copy(bytesRecord, 0, bytesTimeStamp, 0, nTimeStampSize) Dim strTimeStamp = Encoding.UTF8.GetString(bytesTimeStamp) Console.WriteLine("Time : {0}", ConvertEpochTime(Left(strTimeStamp, 8))) ' メッセージ情報取得 Dim bytesFieldHeader(10) As Byte ' フィールドヘッダ取得用 Array.Copy(bytesRecord, 0, bytesTimeStamp, 0, 14) Dim nPos As Integer Dim nHeader As Integer Dim nType As Integer Dim nMessageLength As Integer Dim strName As String Dim byteData() As Byte Dim nDataLength As Integer nPos = nTimeStampSize While nPos < nLength ' フィールドヘッダを読み込み解析処理を行う If (nPos + 3) < nLength Then nHeader = bytesRecord(nPos) nType = bytesRecord(nPos + 1) nMessageLength = bytesRecord(nPos + 2) End If ' 0x02, 0x27 で始まる場合、name として取り扱う If nHeader = &H2 And nType = &H27 Then Dim byteName(nMessageLength - 6 - 1) As Byte Array.Copy(bytesRecord, nPos + 3 + 6, byteName, 0, nMessageLength - 6 - 1) strName = Encoding.UTF8.GetString(byteName) strName = Replace(strName, vbNullChar, "") If strName.Length > 0 Then Console.WriteLine("Name : {0}", strName) End If nPos += 2 + nMessageLength ' 0x03 は読み飛ばす ElseIf nHeader = &H3 Then ' 読み飛ばす nPos += 1 ' 残りはデータ部としてバイト配列に連結する Else If byteData Is Nothing Then nDataLength = 0 Else nDataLength = byteData.Length End If ReDim Preserve byteData(nDataLength) Dim c = bytesRecord(nPos) byteData(nDataLength) = c nPos += 1 End If End While ' データ部をUTF-8でデコードする。一部、特殊記号(外字?)については、ASCII 文字置換。 Dim strArrow As String = Encoding.UTF8.GetString({&HEE, &H81, &HAF}) ' 特殊記号 Dim strData As String = Encoding.UTF8.GetString(byteData) strData = Replace(strData, strArrow, "=>") ' 特殊記号は ASCII 文字で置換 Console.WriteLine("Data : {0}", strData) End Sub ' エポック秒変換 ' 例)"52978F55" -> "2013/11/29 03:45:41" Function ConvertEpochTime(strEpochTime As String) As String Dim strResult As String Dim unixTimeStamp As Integer = Convert.ToInt32(strEpochTime, 16) Dim unixDate As DateTime = (New DateTime(1970, 1, 1)).AddSeconds(unixTimeStamp).ToLocalTime() strResult = unixDate.ToString("yyyy/MM/dd HH:mm:ss") Return strResult End Function End Module
これでバイナリ・ファイルを変数bytesに読み込みます。
Dim bytes = My.Computer.FileSystem.ReadAllBytes("C:\00000001.log")
そのファイルの構造が分からないと、文字列だけ切り出すのは無理です。可変長みたいなので。
文字コードはUTF-8みたいなので、上の回答の"shift_jis"→"utf-8"変更で読み込める部分はあると思います。
FF XIV のデータを解析しようとしてるwww
こちらが参考になるかもしれません。
http://www.reddit.com/r/ffxiv/comments/1vey94/binary_file_format_for_combat_logs/
gizmo5 さんの回答にあった
■ Binary file format for combat logs - ffxiv
http://www.reddit.com/r/ffxiv/comments/1vey94/binary_file_format_for_combat_logs/
を参考に、読み込むサンプルを書いてみました。
(やっつけなので、バグってるかも知れません。)
Imports System Imports System.IO Imports System.Text Module Module1 ' <参考> ' http://i.imgur.com/by3VaAX.png Sub Main() Dim bytesFile() As Byte = File.ReadAllBytes("00000001.log") Dim bytesBody(3) As Byte ' ヘッダ情報取得用 Dim bytesPos(3) As Byte ' ヘッダ情報取得用 Array.Copy(bytesFile, 0, bytesBody, 0, 4) ' 先頭の4バイトを取得 Dim nLength = BitConverter.ToInt32(bytesBody, 0) ' 先頭の4バイトをレコード数として取得する Dim nPos As Integer ' 現在の位置 Dim nPrevPos As Integer ' 一つ前の位置 Dim nSize As Integer ' 1レコードのサイズ Dim i As Integer nPrevPos = 0 ' レコード数分ループする For i = 0 To nLength - 1 ' レコードの位置を取得 Array.Copy(bytesFile, 8 + i * 4, bytesPos, 0, 4) nPos = BitConverter.ToInt32(bytesPos, 0) ' 1レコードのサイズを算出する(現在の位置-1つ前の位置) nSize = nPos - nPrevPos ' 1レコード取得する Dim bytesRecord(nSize - 1) As Byte Array.Copy(bytesFile, 8 + nLength * 4 + nPrevPos, bytesRecord, 0, nSize) ' データを解析する Parse(bytesRecord) ' 一つ前の位置を変数に保持しておく nPrevPos = nPos Next End Sub ' 解析処理 ' <参考> ' http://www.reddit.com/r/ffxiv/comments/1vey94/binary_file_format_for_combat_logs/ Sub Parse(bytesRecord() As Byte) Dim nLength As Integer = bytesRecord.Length ' 日付情報取得 Dim nTimeStampSize = 14 Dim bytesTimeStamp(nTimeStampSize - 1) As Byte ' タイムスタンプ Array.Copy(bytesRecord, 0, bytesTimeStamp, 0, nTimeStampSize) Dim strTimeStamp = Encoding.UTF8.GetString(bytesTimeStamp) Console.WriteLine("Time : {0}", ConvertEpochTime(Left(strTimeStamp, 8))) ' メッセージ情報取得 Dim bytesFieldHeader(10) As Byte ' フィールドヘッダ取得用 Array.Copy(bytesRecord, 0, bytesTimeStamp, 0, 14) Dim nPos As Integer Dim nHeader As Integer Dim nType As Integer Dim nMessageLength As Integer Dim strName As String Dim byteData() As Byte Dim nDataLength As Integer nPos = nTimeStampSize While nPos < nLength ' フィールドヘッダを読み込み解析処理を行う If (nPos + 3) < nLength Then nHeader = bytesRecord(nPos) nType = bytesRecord(nPos + 1) nMessageLength = bytesRecord(nPos + 2) End If ' 0x02, 0x27 で始まる場合、name として取り扱う If nHeader = &H2 And nType = &H27 Then Dim byteName(nMessageLength - 6 - 1) As Byte Array.Copy(bytesRecord, nPos + 3 + 6, byteName, 0, nMessageLength - 6 - 1) strName = Encoding.UTF8.GetString(byteName) strName = Replace(strName, vbNullChar, "") If strName.Length > 0 Then Console.WriteLine("Name : {0}", strName) End If nPos += 2 + nMessageLength ' 0x03 は読み飛ばす ElseIf nHeader = &H3 Then ' 読み飛ばす nPos += 1 ' 残りはデータ部としてバイト配列に連結する Else If byteData Is Nothing Then nDataLength = 0 Else nDataLength = byteData.Length End If ReDim Preserve byteData(nDataLength) Dim c = bytesRecord(nPos) byteData(nDataLength) = c nPos += 1 End If End While ' データ部をUTF-8でデコードする。一部、特殊記号(外字?)については、ASCII 文字置換。 Dim strArrow As String = Encoding.UTF8.GetString({&HEE, &H81, &HAF}) ' 特殊記号 Dim strData As String = Encoding.UTF8.GetString(byteData) strData = Replace(strData, strArrow, "=>") ' 特殊記号は ASCII 文字で置換 Console.WriteLine("Data : {0}", strData) End Sub ' エポック秒変換 ' 例)"52978F55" -> "2013/11/29 03:45:41" Function ConvertEpochTime(strEpochTime As String) As String Dim strResult As String Dim unixTimeStamp As Integer = Convert.ToInt32(strEpochTime, 16) Dim unixDate As DateTime = (New DateTime(1970, 1, 1)).AddSeconds(unixTimeStamp).ToLocalTime() strResult = unixDate.ToString("yyyy/MM/dd HH:mm:ss") Return strResult End Function End Module
実行結果
Time : 2013/11/29 03:45:41 Name : Pibu Parrotking Data : Pibu Parrotkingの「ステディハンドII」 Time : 2013/11/29 03:45:41 Name : Pibu Parrotking Data : => Pibu Parrotkingに「ステディハンドII」の効果。 Time : 2013/11/29 03:45:41 Name : Hermis Forever Data : Hermis Foreverの「インナークワイエット」 Time : 2013/11/29 03:45:41 Name : Hermis Forever Data : => Hermis Foreverに「インナークワイエット」の効果。 Time : 2013/11/29 03:45:42 Name : Porter One Data : Porter Oneの「コンファートゾーン」 Time : 2013/11/29 03:45:42 Name : Porter One Data : => Porter Oneに「コンファートゾーン」の効果。 :
実行結果
2014/03/22 15:26:13