C# - Octave의 "save -binary ..."로 생성한 바이너리 파일 분석
예를 하나 들어볼까요? ^^
>> M = magic(10)
M =
92 99 1 8 15 67 74 51 58 40
98 80 7 14 16 73 55 57 64 41
4 81 88 20 22 54 56 63 70 47
85 87 19 21 3 60 62 69 71 28
86 93 25 2 9 61 68 75 52 34
17 24 76 83 90 42 49 26 33 65
23 5 82 89 91 48 30 32 39 66
79 6 13 95 97 29 31 38 45 72
10 12 94 96 78 35 37 44 46 53
11 18 100 77 84 36 43 50 27 59
이렇게 Octave에서 생성한 데이터를 다른 언어, 여기서는 C#으로 가져오고 싶은 것입니다. 물론, save 명령어를 이용하면 되는데요,
>> save test.dat M
/* 출력 결과
# Created by Octave 8.1.0, Fri Apr 14 23:19:08 2023 GMT
# name: M
# type: matrix
# rows: 10
# columns: 10
92 99 1 8 15 67 74 51 58 40
98 80 7 14 16 73 55 57 64 41
4 81 88 20 22 54 56 63 70 47
85 87 19 21 3 60 62 69 71 28
86 93 25 2 9 61 68 75 52 34
17 24 76 83 90 42 49 26 33 65
23 5 82 89 91 48 30 32 39 66
79 6 13 95 97 29 31 38 45 72
10 12 94 96 78 35 37 44 46 53
11 18 100 77 84 36 43 50 27 59
*/
아쉽게도 텍스트 파싱을 들어가야 합니다. 이게 은근히 좀 ^^ 번거로운 작업을 수반합니다. 사실 텍스트가 사람이 보기에만 편한 것일 뿐 오히려 바이너리로 저장하는 것이 코딩은 더 쉽습니다.
Octave도 이런 옵션을 제공하는데요,
>> save -binary test.dat M
이렇게 출력한 test.dat를 분석하려면 바이너리 포맷을 알아야만 합니다. 다행히 검색해 보면 다음과 같은 글이 있는데요,
Octave binary file format specification
; https://lists.gnu.org/archive/html/help-octave/2004-11/msg00068.html
따라서 대충 코딩을 다음과 같이 할 수 있습니다.
using System.Text;
namespace Octave;
// https://lists.gnu.org/archive/html/help-octave/2004-11/msg00068.html
public struct BinaryOctaveFile
{
public string Magic;
public byte EndianType; /* 0: Little */
public int OctaveVarNameLength; /* length of var name */
public string OctaveVarName; /* var name itself*/
public int DocLength; /* doc length (4 bytes) and doc (0 bytes) */
public bool GlobalFlag; /* global flag (false) */
public byte DataType; /* typically 255 */
public int DataTypeNameLength; /* always 6 */
public string DataTypeName; /* type is always "matrix" */
public int Unknown1; /* FE FF FF FF */
public int Rows;
public int Columns;
public double[] Data;
public byte Unknown2; /* 0x07 */
public static BinaryOctaveFile Read(string filePath)
{
BinaryOctaveFile octave = new BinaryOctaveFile();
using (FileStream fs = File.OpenRead(filePath))
using (BinaryReader sr = new BinaryReader(fs, Encoding.ASCII))
{
octave.Magic = new string(sr.ReadChars(10));
octave.EndianType = sr.ReadByte();
octave.OctaveVarNameLength = sr.ReadInt32();
octave.OctaveVarName = new string(sr.ReadChars(octave.OctaveVarNameLength));
octave.DocLength = sr.ReadInt32();
octave.GlobalFlag = sr.ReadByte() == 1 ? true : false;
octave.DataType = sr.ReadByte();
octave.DataTypeNameLength = sr.ReadInt32();
octave.DataTypeName = new string(sr.ReadChars(octave.DataTypeNameLength));
octave.Unknown1 = sr.ReadInt32();
octave.Rows = sr.ReadInt32();
octave.Columns = sr.ReadInt32();
octave.Unknown2 = sr.ReadByte();
int dataCount = octave.Rows * octave.Columns;
octave.Data = new double[dataCount];
for (int i = 0; i < dataCount; i ++)
{
octave.Data[i] = sr.ReadDouble();
}
}
return octave;
}
}
텍스트 파일로 된 유형보다 훨씬 깔끔하죠? ^^ 자, 그래서 코딩은 다음과 같이 할 수 있습니다.
using Octave;
namespace ConsoleApp1;
internal class Program
{
static void Main(string[] args)
{
BinaryOctaveFile octave = BinaryOctaveFile.Read(@"test.dat");
// 칼럼 우선으로 저장되었기 때문에!
for (int i = 0; i < octave.Columns; i++)
{
for (int j = 0; j < octave.Rows; j++)
{
Console.Write($"{octave.Data[j * octave.Rows + i],3} ");
}
Console.WriteLine();
}
}
}
/* 출력 결과
92 99 1 8 15 67 74 51 58 40
98 80 7 14 16 73 55 57 64 41
4 81 88 20 22 54 56 63 70 47
85 87 19 21 3 60 62 69 71 28
86 93 25 2 9 61 68 75 52 34
17 24 76 83 90 42 49 26 33 65
23 5 82 89 91 48 30 32 39 66
79 6 13 95 97 29 31 38 45 72
10 12 94 96 78 35 37 44 46 53
11 18 100 77 84 36 43 50 27 59
*/
조금만 코딩을 추가하면, C# 데이터를 Octave에서 읽도록 출력하거나, "
MathNet.Numerics" 패키지의 Matrix와 연동하는 것도 가능할 것입니다. ^^
(
첨부 파일은 이 글의 예제 코드를 포함합니다.)
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]