ArcMap提供了掛接Excel表格信息到屬性表的功能,但是當數據量較大到以萬計甚至十萬計的時候這個功能就歇菜了,當然,你可以考慮分段掛接。這個掛接功能只是做了一個表關聯,屬性記錄每個字段的信息需要通過“字段計算器”計算過來。
Excel數據寫入ArcGis屬性表功能開發實例中,博主見到太多使用UpdateCursor的方式,界面卡翔,效率感人。
ArcGis Shapefile的屬性表信息存放在一個dbf格式(dbaseIV,dbf4,dbase4,下稱dbf4)的文件中,那么,有沒有一種方式可以通過直接對它的讀寫實現快速掛接?通過數據庫連接的方式可以實現,但是對客戶機的環境配置有要求,起碼得有OLEDB、ODBC驅動……,這樣整不爽。那么,有沒有一種方式可以借由第三庫或者某種方式去直接解析它呢?博主去分析了這個可能,DBF文件的結構並不復雜,找個比較成熟的輪子來研究解析最好不過,去年,博主在GitHub發現了它——“FastDBF”,地址:https://github.com/SocialExplorer/FastDBF。該庫作者是老外,所以不出意外的對於中文環境下dbf文件的讀寫不友好,有點兒小bug,相關說明可以查找本人博客。
優點:插件方式開發下不操作ArcObject對象,並且可以使用多線程+委托的方式使掛接在子線程進行,進度傳回主線程更新UI,掛接速度快且不影響ArcMap的瀏覽使用。
- dbf4文件格式與解析
本篇不講,可以自行百度或參考:
https://www.clicketyclick.dk/databases/xbase/format/dbf.html
需要提醒的是網上的格式解析說明文章都將dbf4編碼規則默認為ANSI(中文操作系統下是gbk,codepage=936,在非英文操作系統下,這些文章寫為ASCII並不嚴謹)去解析,而實際上ArcGis10.2之后版本生成的dbf文件默認使用了utf-8(codepage65001)編碼。這就牽涉到了“FastDBF”在中文環境下的bug。
- FastDBF讀寫dbf4文件
打開dbf文件,注意選擇字符編碼規則
var odbf = new DbfFile(Encoding.GetEncoding(rdoGBK.Checked ? 936 : 65001)); odbf.Open(dbfPath, FileMode.Open);
讀取記錄數、字段數、長度
var header=odbf.Header; int dbfRecordCount=Convert.ToInt32( header.RecordCount); //header.ColumnCount字段數 for (int i = 0; i < header.ColumnCount; i++) { this.dataGridView2.Rows.Add(); //字段名 this.dataGridView2[0, i].Value = header[i].Name; //字段類型 this.dataGridView2[2, i].Value = header[i].ColumnType.ToString(); //字段長度與小數位 if (header[i].DecimalCount!=0) { this.dataGridView2[3, i].Value = header[i].Length.ToString() + "," + header[i].DecimalCount; } else this.dataGridView2[3, i].Value = header[i].Length.ToString(); }
從DataTable中匹配記錄掛接,Excel裝入DataTable可以使用epplus,不講。
var odbf = new DbfFile(Encoding.GetEncoding(prms.encode)); DbfRecord orec; try { odbf.Open(prms.dbfPath, FileMode.Open); orec = odbf.Read(0); int i = 1; while (orec != null) { DataRow[] dataRows = dt.Select(linkFieldNameExcel + "=" + "'" + orec[prms.linkFieldIndexShp].ToString() + "'"); if (dataRows.Count() > 0) { DataRow dr = dataRows.First(); foreach (var item in prms.updateFieldPrms) { string content=dr[item.fieldNamesExcel].ToString(); int byteCount= Encoding.GetEncoding(prms.encode).GetByteCount(content); if (byteCount<=item.fieldLength) { orec[item.fieldNameDbf] = content; } } odbf.Update(orec); } orec = odbf.ReadNext(); ProgressChanged(this, i); i++; } } catch (Exception) { throw new Exception("dbf掛接過程出錯!"); } finally { dt.Dispose(); orec = null; odbf.Close(); }