首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> C# 入門

用 C# 讀取二進制檔案:結構配置、位元序、Marshal 互通完整指南

     編輯:C# 入門

C# 二進制檔案讀寫:跨平台、高效能、型別安全的完整解決方案

二進制格式在效能、空間、跨語言互通上具優勢。C# 透過 BinaryReader/Writer、Marshal、StructLayout 解決對齊、位元序、型別轉換挑戰。

核心挑戰

挑戰C/C++ 做法C# 解決方案
結構記憶體配置編譯器自動處理StructLayout 屬性控制
位元序 (Endianness)手動位元組交換反射自動檢測/轉換
型別安全讀寫指標強制轉型BinaryReader/Marshal
複雜型別 (字串/陣列)固定大小緩衝區MarshalAs 指定編碼

結構配置控制:StructLayout

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MyData {
    public int Id;
    public short Status;
    public byte Flags;
    // Pack=1 緊密封裝,無對齊填充
}
  • LayoutKind.Sequential:依宣告順序配置
  • LayoutKind.Explicit:配合 FieldOffset 精確控制
  • Pack:對齊邊界 (1, 2, 4, 8, 16),1=緊密

字串封送處理:MarshalAs

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct FileHeader {
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
    public string FileName;  // 固定 50 字元 ANSI 字串
    
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
    public byte[] Reserved;  // 固定大小陣列
}

位元序自動處理:反射基礎轉換

.NET 執行階段可存取型別中繼資料,實作通用位元序轉換:

public static void FixEndianness(object obj) {
    foreach (FieldInfo field in obj.GetType().GetFields()) {
        if (field.FieldType == typeof(ushort) || field.FieldType == typeof(uint)) {
            object val = field.GetValue(obj);
            if (val is ushort u16) field.SetValue(obj, Swap(u16));
            else if (val is uint u32) field.SetValue(obj, Swap(u32));
        }
        // 遞迴處理巢狀結構...
    }
}

static ushort Swap(ushort v) => (ushort)((v >> 8) | (v << 8));
static uint Swap(uint v) => (v >> 24) | ((v >> 8) & 0xFF00) | ((v << 8) & 0xFF0000) | (v << 24);

注意:浮點數通常遵循 IEEE 754,不需交換;負數補數表示法跨平台一致。

二進制讀寫兩大模式

模式一:BinaryReader/Writer (串流導向)

// 寫入
using (var fs = new FileStream("data.bin", FileMode.Create))
using (var bw = new BinaryWriter(fs, Encoding.UTF8)) {
    bw.Write(123);           // int
    bw.Write(3.14f);         // float
    bw.Write("Hello");       // 字串 (長度前綴)
    bw.Write(new byte[10]);  // 位元組陣列
}

// 讀取
using (var fs = new FileStream("data.bin", FileMode.Open))
using (var br = new BinaryReader(fs, Encoding.UTF8)) {
    int i = br.ReadInt32();
    float f = br.ReadSingle();
    string s = br.ReadString();
    byte[] buf = br.ReadBytes(10);
}

模式二:Marshal 結構直接映射 (記憶體導向)

[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Particle {
    public float X, Y, Z;
    public float Vx, Vy, Vz;
    public int Life;
}

public static Particle[] ReadParticles(string path) {
    byte[] data = File.ReadAllBytes(path);
    int count = data.Length / Marshal.SizeOf();
    var particles = new Particle[count];
    
    GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
    try {
        IntPtr ptr = handle.AddrOfPinnedObject();
        for (int i = 0; i < count; i++) {
            particles[i] = Marshal.PtrToStructure(ptr);
            ptr = IntPtr.Add(ptr, Marshal.SizeOf());
        }
    } finally {
        handle.Free();
    }
    return particles;
}

跨平台實務建議

  1. 定義明確協定:文件化結構配置、位元序 (建議小端序)、字串編碼
  2. 版本欄位:結構首欄位放版本號,支援演化
  3. 驗證機制:Magic Number、Checksum、CRC
  4. 測試矩陣:x86/x64、Windows/Linux/macOS、大小端序 CPU
  5. 考慮替代格式:Protocol Buffers、MessagePack、FlatBuffers 提供更好相容性

效能比較

方法速度記憶體適用場景
BinaryReader/Writer中低 (串流)一般檔案、網路傳輸
Marshal 結構映射極快高 (全載入)大量同質結構、遊戲資產
MemoryMappedFile極快共享記憶體超大檔案、進程間通訊
C# 雖有托管限制,但結合 StructLayout、MarshalAs、BinaryReader、MemoryMappedFile,完全能勝任高效能二進制處理,且比 C/C++ 更型別安全、更易維護。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved