Hex文件每一行数据全部由十六进制数字组成,包含 :、数据长度、起始地址、记录类型、数据、校验和六个部分。
上图中,每一行记录都以“:”开头,“20”为后面的数据长度,表示该记录有32字节的数据,“C240”为该行记录的起始地址中的低位,后面的“00”为该记录的类型,
“0060FAE83132333435363738393000000060C2000060FFF7000900020060FFF8”为该记录所包含的全部32字节数据,“B5”为校验和,
此行就是将数据依次刷写到起始地址为“0060C240”的连续地址中,后面会解释“0060”的由来。
每字节数据被编码成2个16进制字符,第一个字符代表数据的高四位,第二个字符代表数据的低4位。
1. 记录类型
Hex文件记录类型包括00、01、02、03、04、05等,每一种都包含不同的含义
00:数据记录
01:文件结束记录
02:扩展段地址记录
03:开始段地址记录
04:扩展线性地址记录
05:开始线性地址记录
上图中截取了三种记录类型04、00和01
第一行中数据长度为02,表示两字节的数据“095B”,04位扩展线性数据记录,扩展地址就是04记录中的数据位“095B”,
该地址就作为一下每行的起始地址中的高位,第二行记录中的地址“0000”为低位,那么第二行记录的起始地址就是“095B0000”,
第三行记录的起始地址就是“095B0020”,“095B”会一直作为高位,直到下一个扩展线性记录的出现,
最后一行为文件记录的结束
2. 起始地址
起始地址一般由高位和低位组成,即04记录中的地址加上00记录中的地址
3. 数据长度
包含一个字节,即后面数据的字节数的十六进制表示
4.校验和
包含一个字节,校验和=0xFF&(0x100-(长度字节+地址字节+类型字节+数据字节))
长度字节+地址字节+类型字节+数据字节=0x20+0x00+0x00+0x00+0x00+...+0xC3+0xC3=0x1880
0x80=0xFF&(0x100-0x1880)
5.实现代码

1 public class Hex 2 { 3 public string path; //选择文件的路径 4 public List<List<string>> listAllline = new List<List<string>>(); //依据区域对数据进行存储,一个扩展性地址放一个list中,存储含有部分记录的Hex文件包括(00、04和01的记录等) 5 6 7 /// <summary> 8 /// 读取Hex文件,将所有记录存储在List中,便于调用修改 9 /// </summary> 10 /// <param name="file_path"></param> 11 /// <returns></returns> 12 public bool Read_Hex(string file_path) 13 { 14 FileStream fileStream = null ; 15 StreamReader streamReader = null; 16 int count = 0; 17 string type; 18 int regionNum = -1; 19 try 20 { 21 fileStream = new FileStream(file_path, FileMode.Open, FileAccess.Read); 22 streamReader = new StreamReader(fileStream, Encoding.Default ); 23 string currentLine = ""; 24 listAllline.Clear(); 25 26 while ((currentLine = streamReader.ReadLine()) != null && currentLine.Trim ()!="") 27 { 28 if(currentLine.Substring(0, 1)==":") 29 { 30 type = currentLine.Substring(7, 2); 31 if (type == "04" || type == "01") 32 { 33 regionNum++; 34 listAllline.Add(new List<string>()); 35 } 36 count++; 37 listAllline[regionNum].Add(currentLine); 38 } 39 40 } 41 Console.WriteLine("文件中共有" + count + "行数据"); 42 Console.WriteLine( regionNum + "块区域"); 43 return true; 44 } 45 catch (Exception ex) 46 { 47 Console.WriteLine(ex.ToString() + count); 48 MessageBox.Show(ex.Message +count.ToString()); 49 return false; 50 } 51 finally 52 { 53 fileStream.Close(); 54 streamReader.Close(); 55 } 56 57 } 58 59 /// <summary> 60 /// 计算一行数据的校验和 61 /// </summary> 62 /// <param name="line">包含:的整行记录 </param> 63 /// <returns></returns> 64 private string Checksum(string line) 65 { 66 string checkSum ; 67 int sum = 0; 68 string[] byteNum = new string[line.Length / 2 ];//计算字节数 69 for (int i = 1; i < line.Length / 2 ; i++) 70 { 71 byteNum[i] = line.Substring(i * 2 + 1, 2);//从长度位将一行数据按照字节分割为字节数组,注意前面的: 72 sum += Convert.ToInt32(byteNum[i], 16);//将数据进行累加 73 } 74 checkSum = Convert.ToString(0xFF & (0x100-sum), 16); //计算校验位 75 return checkSum; 76 } 77 78 /// <summary> 79 /// 将区域内不连续的地址补全 80 /// </summary> 81 /// <param name="completeValue">用于补全的字符</param> 82 /// <returns></returns> 83 public List<List<string>> CompleteFile(string completeValue) 84 { 85 List<List<string>> listTemp = new List<List<string>>();//暂存数据 86 List<List<string>> listCompletion = new List<List<string>>();//用来存储补全后的数据 87 uint currentAdd = 0; 88 uint dataLength = 0; 89 uint lastDataLength = 0; //上一行数据长度 90 uint lastAdd = 0; //上一行地址 91 int completeLine = 0; //计算区域补全的行数 92 int totalComplete = 0; //计算总补全的行数 93 string lastLine=string.Empty ; //上一行的记录 94 string currentLine; 95 96 try 97 { 98 #region 将地址相同高位的记录放入一个list中 99 int a = -1; 100 for (int i = 0; i < listAllline.Count; i++) 101 { 102 a++; 103 listTemp.Add(new List<string>()); 104 string firstLine = listAllline[i][0]; 105 for (int k = 0; k < listAllline[i].Count; k++) 106 { 107 listTemp[a].Add(listAllline[i][k]); 108 } 109 for (int j = i+1; j < listAllline.Count; j++) 110 { 111 if (firstLine == listAllline[j][0]) 112 { 113 for (int k = 1; k < listAllline[j].Count; k++) 114 { 115 listTemp[a].Add(listAllline[j][k]); 116 } 117 i++; 118 } 119 } 120 } 121 #endregion 122 StringBuilder temp = new StringBuilder(); 123 for (int i = 0; i < listTemp.Count; i++) 124 { 125 completeLine = 0; 126 listCompletion.Add(new List<string>()); 127 for (int j = 0; j < listTemp[i].Count; j++) 128 { 129 currentLine = listTemp[i][j]; 130 if (j == 0) 131 { 132 listCompletion[i].Add(currentLine); 133 continue; 134 } 135 if (j == 1) 136 { 137 if(currentLine.Substring(3, 4)=="0000")//第一行的地址低位就是0000 138 { 139 listCompletion[i].Add(currentLine); 140 lastLine = currentLine; 141 lastDataLength = Convert.ToUInt32(currentLine.Substring(1, 2), 16); 142 lastAdd = Convert.ToUInt32(currentLine.Substring(3, 4), 16); 143 continue; 144 } 145 else //第一行的地址低位不是0000,从头开始做补全 146 { 147 temp.Clear(); 148 completeLine++; 149 temp.Clear(); 150 temp.Append(currentLine.Substring(0, 3)); 151 string newAdd_string = "0000"; 152 temp.Append(newAdd_string); 153 for (int k = 0; k < Convert.ToInt32(currentLine.Substring(1, 2),16); k++) 154 { 155 temp.Append(completeValue); 156 } 157 temp.Append(Checksum(temp.ToString())); 158 listCompletion[i].Add(temp.ToString()); 159 160 lastLine = temp.ToString (); 161 lastDataLength = Convert.ToUInt32(lastLine.Substring(1, 2), 16); 162 lastAdd = Convert.ToUInt32(lastLine.Substring(3, 4), 16); 163 } 164 } 165 166 dataLength = Convert.ToUInt32(currentLine.Substring(1, 2), 16); 167 currentAdd = Convert.ToUInt32(currentLine.Substring(3, 4), 16); 168 169 if (lastAdd + lastDataLength < currentAdd) //判断相邻记录地址是否连续 170 { 171 int completeLen = Convert.ToInt32(currentAdd - lastAdd - lastDataLength); //计算需要补全的长度 172 uint newAdd = lastAdd; //每行新记录填补的地址 173 temp.Clear(); 174 while (completeLen > 0) 175 { 176 if (completeLen <= lastDataLength) //需要补的数据长度不足上一行记录的数据长度 177 { 178 completeLine++; 179 temp.Clear(); 180 temp.Append(":"); 181 string newLen = Convert.ToString(completeLen, 16).PadLeft(2, '0').ToUpper(); 182 temp.Append(newLen); 183 string newAdd_string = Convert.ToString(newAdd + completeLen, 16).PadLeft(4, '0').ToUpper(); 184 temp.Append(newAdd_string); 185 for (int k = 0; k < completeLen; k++) 186 { 187 temp.Append(completeValue); 188 } 189 temp.Append(Checksum(temp.ToString())); 190 listCompletion[i].Add(temp.ToString()); 191 completeLen -= completeLen; //减去已补过的长度 192 } 193 else 194 { 195 completeLine++; 196 temp.Clear(); 197 temp.Append(lastLine.Substring(0, 3)); 198 string newAdd_string = Convert.ToString(newAdd + lastDataLength, 16).PadLeft(4, '0').ToUpper(); 199 temp.Append(newAdd_string); 200 for (int k = 0; k < Convert.ToInt32(lastDataLength); k++) 201 { 202 temp.Append(completeValue); 203 } 204 temp.Append(Checksum(temp.ToString())); 205 listCompletion[i].Add(temp.ToString()); 206 completeLen -= Convert.ToInt32(lastDataLength); 207 } 208 newAdd = newAdd + lastDataLength; 209 } 210 211 currentAdd = newAdd + lastDataLength; //当前行的地址 212 listCompletion[i].Add(currentLine); 213 } 214 else 215 { 216 listCompletion[i].Add(currentLine); 217 } 218 lastAdd = currentAdd; //记录上一行地址 219 lastDataLength = dataLength; //记录上一行数据长度 220 lastLine = currentLine; 221 222 if (j == listTemp[i].Count-1) //判断是否到这个list的最后一行 223 { 224 if ((j+ completeLine) < 2048) //一个区域最多有2048行记录, 225 { 226 temp.Clear(); 227 uint newAdd = lastAdd; 228 while ((j + completeLine) < 2048) //开始末尾补全 229 { 230 completeLine++; 231 temp.Clear(); 232 temp.Append(lastLine.Substring(0, 3)); 233 string newAdd_string = Convert.ToString(newAdd + lastDataLength, 16).PadLeft(4, '0').ToUpper(); 234 temp.Append(newAdd_string); 235 for (int k = 0; k < Convert.ToInt32(lastDataLength); k++) 236 { 237 temp.Append(completeValue); 238 } 239 temp.Append(Checksum(temp.ToString())); 240 listCompletion[i].Add(temp.ToString()); 241 newAdd = newAdd + lastDataLength; 242 243 } 244 } 245 } 246 } 247 totalComplete += completeLine; 248 } 249 250 Console.WriteLine("共补全了" + totalComplete + "行"); 251 return listCompletion; 252 } 253 catch(Exception ex) 254 { 255 Console.WriteLine(ex.Message ); 256 MessageBox.Show(ex.ToString ()); 257 return null; 258 } 259 260 } 261 262 /// <summary> 263 /// 检查每行记录数据是否有缺失,是否完整 264 /// </summary> 265 /// <returns></returns> 266 public bool CompleteCheck(string line) 267 { 268 string type = line.Substring(7,2); 269 string length = line.Substring(1,2); 270 string address = line.Substring(3,4); 271 return line.Length == 1 + 2 + 4 + 2 + Convert.ToInt32(length) * 2 + 2 ? true : false; 272 } 273 274 /// <summary> 275 /// 依据地址和数据修改Hex文件 276 /// </summary> 277 /// <param name="startAdd"></param> 278 /// <param name="modifyStr"></param> 279 /// <returns></returns> 280 public bool Modify_hex(string startAdd, string modifyStr) 281 { 282 try 283 { 284 List<int> goalRegionList = new List<int>(); //存储具有地址高位的记录区域 285 int modifyLen = modifyStr.Length; //修改的数据长度,判断修改是否完成 286 string add_High = startAdd.Substring(0, 4); 287 string add_Low = startAdd.Substring(4, 4); 288 for (int i = 0; i < listAllline.Count; i++) 289 { 290 if (add_High == listAllline[i][0]) 291 { 292 goalRegionList.Add(i); 293 } 294 } 295 296 uint add_Low_uint = Convert.ToUInt32(add_Low, 16); 297 string currentLine = string.Empty; 298 uint currentAdd_Low_unit; 299 for (int k = 0; k < goalRegionList.Count; k++) 300 { 301 for (int i = 1; i < listAllline[goalRegionList[k]].Count; i++) 302 { 303 currentLine = listAllline[goalRegionList[k]][i]; 304 if (currentLine.Length <= 11) //跳过01的记录 305 { 306 continue; 307 } 308 currentAdd_Low_unit = Convert.ToUInt32(currentLine.Substring(3, 4), 16); 309 if (add_Low_uint >= currentAdd_Low_unit && Convert.ToUInt32(add_Low_uint - currentAdd_Low_unit) < Convert.ToUInt32(currentLine.Substring(1, 2), 16))//找到需要修改的数据的起始地址所在行 310 { 311 while (modifyLen > 0) 312 { 313 currentAdd_Low_unit = Convert.ToUInt32(currentLine.Substring(3, 4), 16); 314 int differenceAdd = Convert.ToInt32(add_Low_uint - currentAdd_Low_unit); //一行中需修改的起始地址之前的数据地址 315 int modifyAdd = Convert.ToInt32(currentLine.Substring(3, 4), 16) * 2 - differenceAdd * 2;//每行中需要修改的数据长度 316 317 if (Convert.ToInt32(currentLine.Substring(3, 4), 16) - differenceAdd >= modifyLen / 2)//判断需要修改的数据地址是不是在同一行 318 { 319 string str1 = currentLine.Remove(9 + (differenceAdd) * 2, modifyLen);//从修改的起始地址位置开始移除旧数据,长度为需要修改的数据长度,12 = 类型2字符+长度2字符+地址8字符 320 string str2 = str1.Insert(9 + (differenceAdd) * 2, modifyStr);//将新数据插入当前记录行 321 string sum_string = Checksum(str2.Remove(str2.Length - 2));//计算新的校验位字节 322 currentLine = str2.Remove(str2.Length - 2, 2) + sum_string;//移除旧校验位,加入新校验位 323 modifyLen = modifyLen - modifyAdd;//正常情况下,修改的数据地址在同一行,此时计算之后modifyLen=0,跳出循环 324 } 325 else //地址不在同一行 326 { 327 string str1 = currentLine.Remove(9 + (differenceAdd) * 2, Convert.ToInt32(currentLine.Substring(1, 2), 16) * 2 - differenceAdd * 2);//移除地址后面的所有数据和校验位 328 string str2 = str1.Insert(9 + (differenceAdd) * 2, modifyStr.Substring(0, (Convert.ToInt32(currentLine.Substring(1, 2), 16) - (differenceAdd)) * 2));//加入新数据 329 string sum_string = Checksum(str2.Remove(str2.Length - 2)); 330 currentLine = str2.Remove(str2.Length - 2, 2) + sum_string; 331 modifyLen = modifyLen - modifyAdd;//计算剩余需要修改的数据长度 332 modifyStr = modifyStr.Remove(0, modifyAdd);//移除已经修改的数据 333 startAdd = startAdd + (Convert.ToUInt32(currentLine.Substring(1, 2), 16) - Convert.ToUInt32(differenceAdd));//计算剩余数据的起始地址 334 } 335 } 336 } 337 } 338 } 339 340 return true; 341 } 342 catch(Exception ex) 343 { 344 Console.WriteLine(ex.Message); 345 return false; 346 } 347 } 348 349 /// <summary> 350 /// 生成新的Hex文件 351 /// </summary> 352 /// <param name="newFilepath"></param> 353 /// <returns></returns> 354 public bool Write_Hex(string newFilepath) 355 { 356 StreamWriter Stream_Writer = null; 357 try 358 { 359 Stream_Writer = new StreamWriter(newFilepath + ".hex", false); 360 for (int i = 0; i < listAllline.Count; i++) 361 { 362 for (int j = 0; j < listAllline[i].Count; j++) 363 { 364 Stream_Writer.WriteLine(listAllline[i][j]); 365 } 366 } 367 return true; 368 } 369 catch(Exception ex) 370 { 371 Console.WriteLine(ex.Message ); 372 return false; 373 } 374 finally 375 { 376 Stream_Writer.Close(); 377 } 378 }
如有错误,欢迎指正