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 }
如有錯誤,歡迎指正