
上一篇提到,輸出的結果中,欄位的名稱可以正常顯示,內容的值卻會變成亂碼。
問題出在哪
上網找解法的時候發現有人遇到相似的狀況,但不同的是,我和對方的亂碼顯示是相反的:他的亂碼會出現在欄位名稱,內容的值反而是正常的。
雖然狀況有點不相同,但他在閱讀了原始碼後提出了一個觀點:
對比 GetName
和 GetFieldAsString
兩個函數可以很明顯看出來,GetFieldAsString
通過調用 Ogr.Utf8BytesToString
將返回的 UTF8 編碼的位元組數組以 UTF8 方式解碼為字元串,所以能夠正常顯示;而 GetName
則直接返回字元串(實際上編譯器隱性調用了 Encoding.Default.GetString
解碼為字元串),由於沒有使用 UTF8 解碼導致顯示為亂碼。
― gis0911178, CSDN / CC 4.0 BY-SA 協議
觀察原始碼
為了證明,我利用反編譯的工具來檢視 Gdal.Core.dll
,可以看到像下面這樣的內容:


從這兩張圖中可以看到,讀取內容值,調用 Feature.GetFieldAsString()
的時候,會將結果進行轉換,Utf8BytesToString()
的內容包含:

所以從結論來看,他的情況應該是正常的,只是不知道為甚麼我會是相反的狀況:有用 UTF8 解碼的內容,反而會是亂碼 😕
Gdal.Core
→ MaxRev.Gdal.Core
所以,按照他的假設,應該是要在取得欄位名稱的時候多調用 Utf8BytesToString()
來讓字串正確顯示,但對於我的狀況來說,反而是要把 Utf8BytesToString()
拿掉 😢 但我對於 dll 相關的技能樹是未開通的狀態,所以選擇放棄 Gdal.Core
這個套件,轉而使用另外一個同樣使用 Gdal,但專門開發給 .NET Core 使用的套件-- MaxRev.Gdal.Core
。
套件的使用方法和原本的 Gdal.Core
一模一樣,只是要在原本的 Gdal.AllRegister();
之前,加上 GdalBase.ConfigureAll();
就可以了。更改套件之後運行的結果如下:

修正中文亂碼
將情境還原之後就可以針對他所提出的方法進行修正了。我選擇最快也最好處理的方法--直接調用原本 dll 中的方法再另外包一層 UTF8 解碼,完整的程式碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
| // 註冊 MaxRev.Gdal.Core
GdalBase.ConfigureAll();
// 註冊 GDAL
Gdal.SetConfigOption("SHAPE_ENCODING", "big5");
Gdal.AllRegister();
Ogr.RegisterAll();
// 開啟 SHP 檔案(0 - 讀取、1 - 讀寫)
var source = Ogr.Open("files/simple/test.shp", (int)EnumShapefileOpen.ReadOnly);
var layerCount = source.GetLayerCount();
for (var i = 0; i < layerCount; i++)
{
// 讀取圖層 Layer 資訊
var layer = source.GetLayerByIndex(i);
var fieldCount = layer.GetLayerDefn().GetFieldCount();
for (var j = 0; j < fieldCount; j++)
{
var field = layer.GetLayerDefn().GetFieldDefn(j);
var fieldName = GetFieldDfnName(field);
Console.WriteLine($"欄位{j + 1}. {fieldName}");
}
// 讀取要素 Feature
Feature feature;
while ((feature = layer.GetNextFeature()) != null)
{
var id = feature.GetFieldAsInteger(0);
Console.WriteLine($"內容1. {id}");
var name = feature.GetFieldAsString(1);
Console.WriteLine($"內容2. {name}");
var area = feature.GetFieldAsInteger64(2);
Console.WriteLine($"內容3. {area}");
var geometry = feature.GetGeometryRef();
geometry.ExportToWkt(out var wkt);
Console.WriteLine($"- WKT: {wkt}");
}
}
static string? Utf8BytesToString(IntPtr ptr)
{
if (ptr == IntPtr.Zero)
return null;
var ms = new MemoryStream();
byte b;
var ofs = 0;
while ((b = Marshal.ReadByte(ptr, ofs++)) != 0)
{
ms.WriteByte(b);
}
return Encoding.UTF8.GetString(ms.ToArray());
}
// Dll 的位置要注意
[DllImport("gdal/x64/gdal305.dll", EntryPoint = "OGR_Fld_GetNameRef", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr OGR_Fld_GetNameRef(HandleRef handle);
static string GetFieldDfnName(FieldDefn fieldDefn)
{
HandleRef handle = FieldDefn.getCPtr(fieldDefn);
IntPtr ptr = OGR_Fld_GetNameRef(handle);
return Utf8BytesToString(ptr);
}
public enum EnumShapefileOpen
{
ReadOnly
}
|
執行之後就可以看到我們把這個亂碼修好啦!

參考文章