海图文件和其更新文件,其结构符合ISO/IEC 8211标准。ISO/IEC 8211是一个以文件为基础的交换格式,该格式包含一个"数据描述记录(Data Descriptive Record [DDR])"和若干个"数据记录(Data Record [DR])"。
DDR和DR数据结构一样,都包含三部分内容:
-
头标区(Leader):包括读取记录和相关参数。
- DDR头标区
起始位 长度 项目名 内容 0 5 记录长度 记录中的字节数 5 1 交换级别 ”3“ 6 1 头标区标识符 ”L“ 7 1 代码指示符 ”E“ 8 1 版本号 ”1“ 9 1 应用指示符 □ 10 2 字段控制长度 ”09“ 12 5 字段区基地址 字段区起始地址(即头标区和目录区字节数之和) 17 3 扩充字符集指示符 □!□ 20 4 入口图 见下表 DDR头标区入口图:
起始位 长度 子项目名 内容 20 1 字段长度字段的大小 取值范围1~9(由编码者定义) 21 1 字段定位字段的大小 取值范围1~9(由编码者定义) 22 1 保留字 ”0“ 23 1 字段标识字段的大小 ”4“ 为查看方便,在此推荐VSCode并安装一款插件hexdump for VSCode。安装完成后便可以二进制查看任何文件。我们挑选一幅最小的图用作测试,用VS Code的二进制方式将US4AK7IM.000打开,如下图所示,可以看出,文件中仍有部分数据是以字符串编码的。
加载上述海图,并利用DDR头标区第5到11个字节组成的控制字符来判断文件是否是合法的电子海图文件。DDR头标区解析代码如下:
public class S57DDRLeader { public int RecordLength; //0-5 public char InterchangeLevel; //5-1 ”3“ public char LeaderIdentifier; //6-1 "L" public char CodeExtensionIndicator; //7-1 "E" public char VersionNumber; //8-1 "1" public char ApplicationIndicator; //9-1 □ public string FieldControlLength; //10-2 2 "09" public int FieldAreaBaseAddress; //12-5 Start adddress of field area public string ExCharacterSetIndicator; //17-3 3 □!□ public int FieldLengthSize; //20-1 1~9 public int FieldPositionSize; //21-1 1~9 public int Reserved; //22-1 0 public int FieldTagSize; //23-1 4 public int EntryMapWidth; //计算字段 入口图字段宽度 public string FieldControlString; //计算字段 控制字符串,判断文件合法性 public S57DDRLeader() { RecordLength = BytesHelper.GetInteger(5); InterchangeLevel = BytesHelper.GetChar(); LeaderIdentifier = BytesHelper.GetChar(); CodeExtensionIndicator = BytesHelper.GetChar(); VersionNumber = BytesHelper.GetChar(); ApplicationIndicator = BytesHelper.GetChar(); FieldControlLength = BytesHelper.GetString(2); FieldAreaBaseAddress = BytesHelper.GetInteger(5); ExCharacterSetIndicator = BytesHelper.GetString(3); FieldLengthSize = BytesHelper.GetInteger(1); FieldPositionSize = BytesHelper.GetInteger(1); Reserved = BytesHelper.GetInteger(1); FieldTagSize = BytesHelper.GetInteger(1); EntryMapWidth = FieldTagSize + FieldLengthSize + FieldPositionSize; FieldControlString = InterchangeLevel.ToString()+LeaderIdentifier.ToString() + CodeExtensionIndicator.ToString() + VersionNumber.ToString() + ApplicationIndicator.ToString() + FieldControlLength; } }
检测工作是否正常:
static void Main(string[] args) { BytesHelper.Load("..\\US4AK7IM.000"); var dl = new S57DDRLeader(); var header = dl.RecordLength.ToString() + dl.FieldControlString + dl.FieldAreaBaseAddress.ToString() + dl.ExCharacterSetIndicator + dl.FieldLengthSize.ToString() + dl.FieldPositionSize.ToString() + dl.Reserved.ToString() + dl.FieldTagSize.ToString(); Console.WriteLine(header); Console.ReadKey(); }
返回结果如图,其与用二进制查看器得到的结果一致,说明DDR头标区解析正确。
- DR头标区
与DDR头标区基本一样,仅指示类之类的字符有差别,解析过程可利用DDR头标区的代码。
起始位 长度 项目名 内容 0 5 记录长度 记录中的字节数 5 1 交换级别 □ 6 1 头标区标识符 ”D“ 7 1 代码指示符 □ 8 1 版本号 □ 9 1 应用指示符 □ 10 2 字段控制长度 □□ 12 5 字段区基地址 字段区起始地址(即头标区和目录区字节数之和) 17 3 扩充字符集指示符 □□□ 20 4 入口图 见下表 DR头标区入口图:
起始位 长度 子项目名 内容 20 1 字段长度字段的大小 取值范围1~9(由编码者定义) 21 1 字段定位字段的大小 取值范围1~9(由编码者定义) 22 1 保留字 ”0“ 23 1 字段标识字段的大小 ”4“ 因此将S57DDRLeader重命名为S57Leader:
public class S57DDRLeader { ...... }