Hex文件格式解析及读写修改代码实现
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 }
View Code
如有错误,欢迎指正