CmpareMode.cpp 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030
  1. #include "CmpareMode.h"
  2. #include "Encode.h"
  3. #include "rcglobal.h"
  4. #include <QFile>
  5. #include <QFileDevice>
  6. #include <QVector>
  7. #include <QCryptographicHash>
  8. #include <functional>
  9. #include <QDataStream>
  10. #include <QtConcurrent>
  11. CmpareMode::CmpareMode()
  12. {
  13. }
  14. CmpareMode::~CmpareMode()
  15. {
  16. }
  17. //识别文字编码,并将文字按照原始编码格式,转换为QString。如果失败,默认按照utf8的格式进行转换;
  18. bool CmpareMode::recognizeTextCode(QByteArray & text, LineFileInfo &lineInfo, QString &outUnicodeText)
  19. {
  20. int lineNums = lineInfo.lineNums;
  21. int length = text.count();
  22. int result = false;
  23. //第一行时,检测一下文件编码,返回值也是文件的编码
  24. if (0 == lineNums)
  25. {
  26. int skip = 0;
  27. lineInfo.code = Encode::DetectEncode((uchar*) text.data(), length, skip);
  28. //根据编码跳过第一行前面的几个字符编码标识字段
  29. if (skip > 0)
  30. {
  31. text = text.mid(skip);
  32. }
  33. return Encode::tranStrToUNICODE((CODE_ID)lineInfo.code, text.data(), text.count(), outUnicodeText);
  34. }
  35. else
  36. {
  37. /*对于头部没有标识的行,需要每行进行详细检查,比较耗时
  38. *对于第一行已经是GBK的编码,标识出所有的确是GBK的行号
  39. *严格来说,如果以后要做国际版,不应该只考虑GBK,而是要考虑本地ASNI编码。
  40. *对中国而言,本地ASNI编码是GBK,对其它国家,比如日本/韩国而言,这些ASNI是它们本国
  41. *对应的本地编码。
  42. */
  43. //#if 0
  44. // //全部都在ascii范围以内,就作为ascii码。注意ASCII处理时其它地方时按照UTF8进行编码的
  45. // if (Encode::CheckTextIsAllAscii((uchar*)text.data(), length))
  46. // {
  47. // lineInfo.code = CODE_ID::ASCII;
  48. // return Encode::tranStrToUNICODE((CODE_ID)lineInfo.code, text.data(), length, outUnicodeText);
  49. // }
  50. // else
  51. // {
  52. //#endif
  53. CODE_ID actualCode = Encode::CheckUnicodeWithoutBOM((uchar*)text.data(), length, outUnicodeText);
  54. if (CODE_ID::UTF8_NOBOM == actualCode)
  55. {
  56. lineInfo.code = CODE_ID::UTF8_NOBOM;
  57. result = true;
  58. }
  59. else if (CODE_ID::GBK == actualCode)
  60. {
  61. //如果发现存在GBK,则要以GBK作为字符编码。这里识别gbk是因为显示的时候,需要转化gbk进行显示
  62. lineInfo.code = CODE_ID::GBK;
  63. result = true;
  64. }
  65. else if (CODE_ID::ANSI == actualCode)
  66. {
  67. lineInfo.code = CODE_ID::UNKOWN; //这里就是乱码了。即不是utf8也不是GBK,也不能说乱码,目前其它国家未处理的码
  68. result = false;
  69. }
  70. //#if 0
  71. // }
  72. //#endif
  73. }
  74. return result;
  75. }
  76. CODE_ID CmpareMode::getTextFileEncodeType(uchar* fileFpr, int fileLength, QString filePath)
  77. {
  78. if (fileLength >= 2 && fileFpr[0] == 0xFF && fileFpr[1] == 0xFE)
  79. {
  80. return CODE_ID::UNICODE_LE; //skip 2
  81. }
  82. else if (fileLength >= 2 && fileFpr[0] == 0xFE && fileFpr[1] == 0xFF)
  83. {
  84. return CODE_ID::UNICODE_BE; //skip 2
  85. }
  86. else if (fileLength >= 3 && fileFpr[0] == 0xEF && fileFpr[1] == 0xBB && fileFpr[2] == 0xBF)
  87. {
  88. return CODE_ID::UTF8_BOM; //skip 3 with BOM
  89. }
  90. //走到这里说明没有文件头BOM,进行全盘文件扫描
  91. if (!filePath.isEmpty())
  92. {
  93. return scanFileRealCode(filePath);
  94. }
  95. return CODE_ID::UNKOWN;
  96. }
  97. //20210802:发现如果是CODE_ID::UNICODE_LE,\r\n变成了\r\0\n\0,读取readLine遇到\n就结束了,而且toUnicode也会变成乱码失败
  98. //所以UNICODE_LE需要单独处理。该函数只处理Unicode_LE编码文件,事先一定要检查文件编码
  99. CODE_ID CmpareMode::readLineFromFileWithUnicodeLe(uchar* m_fileFpr, const int fileLength, QList<LineFileInfo>& lineInfoVec, QList<LineFileInfo>& blankLineInfoVec,int mode, int &maxLineSize)
  100. {
  101. QCryptographicHash md4(QCryptographicHash::Md4);
  102. int lineNums = 0;
  103. CODE_ID code = CODE_ID::UNICODE_LE;
  104. int lineStartPos = 2; //uicode_le前面有2个特殊标识,故跳过2
  105. //获取一行在文件中
  106. auto getOneLineFromFile = [m_fileFpr](int& startPos, const int fileLength, QByteArray& ret)->bool{
  107. if (startPos < fileLength)
  108. {
  109. ret.clear();
  110. int lineLens = 0;
  111. bool isFindLine = false;
  112. for (int i = startPos; i < fileLength; ++i,++lineLens)
  113. {
  114. //遇到换行符号
  115. if (m_fileFpr[i] == 0x0A)
  116. {
  117. //lineLens需要加2,因为当前这个没有加,而且后面还有一个\0,这是le格式规定的
  118. ret.append((char*)(m_fileFpr + startPos), lineLens + 2);
  119. startPos += lineLens + 2;
  120. isFindLine = true;
  121. break;
  122. }
  123. }
  124. //没有找到一行
  125. if (!isFindLine)
  126. {
  127. //最后一行,可能没有带\r\0直接返回
  128. ret.append((char*)(m_fileFpr + startPos), fileLength - startPos);
  129. startPos = fileLength;
  130. }
  131. return true;
  132. }
  133. return false;
  134. };
  135. QByteArray line;
  136. auto work = [mode, &md4](LineFileInfo& lineInfo, const int n) {
  137. if (mode == 0)
  138. {
  139. md4.addData(lineInfo.unicodeStr.trimmed().toUtf8());
  140. }
  141. else if (mode == 1)
  142. {
  143. md4.addData(lineInfo.unicodeStr.left(lineInfo.unicodeStr.length() - n).toUtf8());
  144. }
  145. else if (mode == 2)
  146. {
  147. QString temp = lineInfo.unicodeStr;
  148. md4.addData(temp.replace(QRegExp("\\s"), QString("")).toUtf8());
  149. }
  150. };
  151. while (getOneLineFromFile(lineStartPos, fileLength,line)) {
  152. LineFileInfo lineInfo;
  153. lineInfo.lineNums = lineNums;
  154. /* 这种方式读取文件会包含后面的行尾 */
  155. int length = line.length();
  156. if (maxLineSize < length)
  157. {
  158. maxLineSize = length;
  159. }
  160. //如果是头部有标识的格式,则后续不用详细检查每行编码,直接按照头部标识走
  161. Encode::tranStrToUNICODE(code, line.data(), line.count(), lineInfo.unicodeStr);
  162. lineInfo.code = code;
  163. if (lineInfo.unicodeStr.endsWith("\r\r\n"))
  164. {
  165. //这里是一种错误,但确实可能出现
  166. if (length > 3)
  167. {
  168. /*if (mode == 0)
  169. {
  170. md4.addData(lineInfo.unicodeStr.trimmed().toUtf8());
  171. }
  172. else if (mode == 1)
  173. {
  174. md4.addData(lineInfo.unicodeStr.left(lineInfo.unicodeStr.length() - 3).toUtf8());
  175. }*/
  176. work(lineInfo, 3);
  177. }
  178. else
  179. {
  180. //空白行
  181. lineInfo.isLcsExist = false;
  182. lineInfo.isEmptyLine = true;
  183. }
  184. lineInfo.lineEndFormat = RC_LINE_FORM::DOS_LINE;
  185. }
  186. else if (lineInfo.unicodeStr.endsWith("\r\n"))
  187. {
  188. if (length > 2)
  189. {
  190. /*if (mode == 0)
  191. {
  192. md4.addData(lineInfo.unicodeStr.trimmed().toUtf8());
  193. }
  194. else if(mode == 1)
  195. {
  196. md4.addData(lineInfo.unicodeStr.left(lineInfo.unicodeStr.length() - 2).toUtf8());
  197. }*/
  198. work(lineInfo, 2);
  199. }
  200. else
  201. {
  202. //空白行
  203. lineInfo.isLcsExist = false;
  204. lineInfo.isEmptyLine = true;
  205. }
  206. lineInfo.lineEndFormat = RC_LINE_FORM::DOS_LINE;
  207. }
  208. else if (lineInfo.unicodeStr.endsWith("\n"))
  209. {
  210. if (length > 1)
  211. {
  212. /*if (mode == 0)
  213. {
  214. md4.addData(lineInfo.unicodeStr.trimmed().toUtf8());
  215. }
  216. else if (mode == 1)
  217. {
  218. md4.addData(lineInfo.unicodeStr.left(lineInfo.unicodeStr.length() - 1).toUtf8());
  219. }*/
  220. work(lineInfo, 1);
  221. }
  222. else
  223. {
  224. lineInfo.isLcsExist = false;
  225. lineInfo.isEmptyLine = true;
  226. }
  227. lineInfo.lineEndFormat = RC_LINE_FORM::UNIX_LINE;
  228. }
  229. else if (lineInfo.unicodeStr.endsWith("\r"))
  230. {
  231. if (length > 1)
  232. {
  233. /* if (mode == 0)
  234. {
  235. md4.addData(lineInfo.unicodeStr.trimmed().toUtf8());
  236. }
  237. else if (mode == 1)
  238. {
  239. md4.addData(lineInfo.unicodeStr.left(lineInfo.unicodeStr.length() - 1).toUtf8());
  240. }*/
  241. work(lineInfo, 1);
  242. }
  243. else
  244. {
  245. lineInfo.isLcsExist = false;
  246. lineInfo.isEmptyLine = true;
  247. }
  248. lineInfo.lineEndFormat = RC_LINE_FORM::MAC_LINE;
  249. }
  250. else
  251. {
  252. if (length > 0)
  253. {
  254. /*if (mode == 0)
  255. {
  256. md4.addData(lineInfo.unicodeStr.trimmed().toUtf8());
  257. }
  258. else if (mode == 1)
  259. {
  260. md4.addData(lineInfo.unicodeStr.toUtf8());
  261. }*/
  262. work(lineInfo, 0);
  263. }
  264. else
  265. {
  266. lineInfo.isLcsExist = false;
  267. lineInfo.isEmptyLine = true;
  268. }
  269. lineInfo.lineEndFormat = RC_LINE_FORM::UNKNOWN_LINE;
  270. }
  271. if (lineInfo.isEmptyLine)
  272. {
  273. blankLineInfoVec.append(lineInfo);
  274. }
  275. else
  276. {
  277. lineInfo.md4 = md4.result();
  278. //qDebug() << lineInfo.md4;
  279. md4.reset();
  280. lineInfoVec.append(lineInfo);
  281. }
  282. ++lineNums;
  283. }
  284. return code;
  285. }
  286. //读取每一行,将空白行和非空白行分开。非空白行取他们的行md4值(不包含尾部的换行符)
  287. //返回值:文件扫描出来的字符编码
  288. //在对比行的md5值时,忽略了后面的行尾类型。即只对比字符内容,忽略了行尾。
  289. //20210802:发现如果是CODE_ID::UNICODE_LE,\r\n变成了\r\0\n\0,读取readLine遇到\n就结束了,而且toUnicode也会变成乱码失败
  290. //所以UNICODE_LE需要单独处理。注意UNICODE_BE没有这个问题,因为BE是\0\r\0\n,0在前面就没有这个问题
  291. //20210901 发现使用readLine的方式来读取一行不可靠。因为有些文件中一行中间有个\r,这种情况没有识别为多行。readLine是根据\n来识别的。
  292. //进而导致中间的\r没有识别为多行,但是在编辑器中却多一行,导致对比错误。还是要自己来识别行。不依赖于readLine
  293. //CODE_ID fileCode 事先预判定的编码
  294. CODE_ID CmpareMode::readLineFromFile(uchar* m_fileFpr, const int fileLength, const CODE_ID fileCode, QList<LineFileInfo>&lineInfoVec, QList<LineFileInfo>&blankLineInfoVec, int mode, int &maxLineSize)
  295. {
  296. QCryptographicHash md4(QCryptographicHash::Md4);
  297. int lineNums = 0;
  298. CODE_ID code = fileCode;
  299. bool isExistGbk = false;
  300. bool isExistUnKownCode = false;
  301. bool isExistUtf8 = false;
  302. int lineStartPos = 0;
  303. //跳过前面的BOM头部。LE不在这里处理,在外面
  304. if (fileCode == CODE_ID::UNICODE_BE || fileCode == CODE_ID::UNICODE_LE)
  305. {
  306. lineStartPos = 2;
  307. }
  308. else if (fileCode == CODE_ID::UTF8_BOM)
  309. {
  310. lineStartPos = 3;
  311. }
  312. //获取一行在文件中
  313. auto getOneLineFromFile = [m_fileFpr](int& startPos, const int fileLength, const CODE_ID fileCode, QByteArray& ret)->bool {
  314. if (startPos < fileLength)
  315. {
  316. ret.clear();
  317. int lineLens = 0;
  318. bool isFindLine = false;
  319. for (int i = startPos; i < fileLength; ++i, ++lineLens)
  320. {
  321. //遇到符号CR
  322. if (m_fileFpr[i] == 0x0D)
  323. {
  324. //后一个是LF,即以CRLF结尾
  325. if ((i + 1 < fileLength) && (m_fileFpr[i+1] == 0x0A))
  326. {
  327. //lineLens需要加2,因为当前这个没有加,而且后面还有一个\n
  328. ret.append((char*)(m_fileFpr + startPos), lineLens + 2);
  329. startPos += lineLens + 2;
  330. isFindLine = true;
  331. break;
  332. }
  333. else if ((fileCode == UNICODE_BE)&&((i>0) && m_fileFpr[i-1] == '\0'))
  334. {
  335. //事先发现就是BE格式,以\0\r\0\n为结尾的
  336. if ((i + 2 < fileLength) && (m_fileFpr[i + 1] == 0x0) && (m_fileFpr[i + 2] == 0x0A))
  337. {
  338. //lineLens需要加3,因为当前这个没有加,而且后面还有一个\0\n
  339. ret.append((char*)(m_fileFpr + startPos), lineLens + 3);
  340. startPos += lineLens + 3;
  341. isFindLine = true;
  342. break;
  343. }
  344. else
  345. {
  346. //虽然说是BE格式,但是后面没有以\0\n结尾,而是以\r结尾。这种多半就是错误。直接按\0\r结尾
  347. //lineLens需要加1,因为当前这个没有加
  348. ret.append((char*)(m_fileFpr + startPos), lineLens + 1);
  349. startPos += lineLens + 1;
  350. isFindLine = true;
  351. break;
  352. }
  353. }
  354. else
  355. {
  356. //直接以\r结尾了,后面没有\n或者\0\n。符合MAC格式,windows可能编码只有\r,没有\n的错误情况。
  357. //lineLens需要加1,因为当前这个没有加
  358. ret.append((char*)(m_fileFpr + startPos), lineLens + 1);
  359. startPos += lineLens + 1;
  360. isFindLine = true;
  361. break;
  362. }
  363. }
  364. else if(m_fileFpr[i] == 0x0A)
  365. {
  366. //没有先遇到\r,直接遇到\n.20210903发现忘记处理该情况le
  367. //lineLens需要加1,因为当前这个没有加
  368. ret.append((char*)(m_fileFpr + startPos), lineLens + 1);
  369. startPos += lineLens + 1;
  370. isFindLine = true;
  371. break;
  372. }
  373. }
  374. //没有找到一行
  375. if (!isFindLine)
  376. {
  377. //最后一行,可能没有带\r\0直接返回
  378. ret.append((char*)(m_fileFpr + startPos), fileLength - startPos);
  379. startPos = fileLength;
  380. }
  381. return true;
  382. }
  383. return false;
  384. };
  385. QByteArray line;
  386. auto work = [mode,&md4](LineFileInfo& lineInfo, const int n) {
  387. if (mode == 0)
  388. {
  389. md4.addData(lineInfo.unicodeStr.trimmed().toUtf8());
  390. }
  391. else if (mode == 1)
  392. {
  393. md4.addData(lineInfo.unicodeStr.left(lineInfo.unicodeStr.length() - n).toUtf8());
  394. }
  395. else if (mode == 2)
  396. {
  397. QString temp = lineInfo.unicodeStr;
  398. md4.addData(temp.replace(QRegExp("\\s"), QString("")).toUtf8());
  399. }
  400. };
  401. while (getOneLineFromFile(lineStartPos, fileLength, code, line)) {
  402. LineFileInfo lineInfo;
  403. lineInfo.lineNums = lineNums;
  404. /* 这种方式读取文件会包含后面的行尾 */
  405. int length = line.length();
  406. if (maxLineSize < length)
  407. {
  408. maxLineSize = length;
  409. }
  410. //外面必须把code先检测好了
  411. //if (fileCode == CODE_ID::UNICODE_BE /*|| fileCode == CODE_ID::UNICODE_LE */ || fileCode == CODE_ID::UTF8_BOM)
  412. if(fileCode != CODE_ID::UNKOWN)
  413. {
  414. //如果是头部有标识的格式,则后续不用详细检查每行编码,直接按照头部标识走
  415. Encode::tranStrToUNICODE(code, line.data(), line.count(), lineInfo.unicodeStr);
  416. lineInfo.code = fileCode;
  417. }
  418. else if(fileCode == CODE_ID::UNKOWN)
  419. {
  420. /*对于头部没有标识的行,需要每行进行详细检查,比较耗时
  421. *对于第一行已经是GBK的编码,标识出所有的确是GBK的行号
  422. *严格来说,如果以后要做国际版,不应该只考虑GBK,而是要考虑本地ASNI编码。
  423. *对中国而言,本地ASNI编码是GBK,对其它国家,比如日本/韩国而言,这些ASNI是它们本国
  424. *对应的本地编码。
  425. */
  426. recognizeTextCode(line, lineInfo, lineInfo.unicodeStr);
  427. if (CODE_ID::UTF8_NOBOM == lineInfo.code)
  428. {
  429. isExistUtf8 = true;
  430. }
  431. else if (CODE_ID::GBK == lineInfo.code)
  432. {
  433. //如果发现存在GBK,则要以GBK作为字符编码。这里识别gbk是因为显示的时候,需要转化gbk进行显示
  434. isExistGbk = true;
  435. }
  436. else if (CODE_ID::UNKOWN == lineInfo.code)
  437. {
  438. isExistUnKownCode = true;
  439. }
  440. }
  441. if (lineInfo.unicodeStr.endsWith("\r\r\n"))
  442. {
  443. //这里是一种错误,但确实可能出现
  444. if (length > 3)
  445. {
  446. work(lineInfo,3);
  447. }
  448. else
  449. {
  450. //空白行
  451. lineInfo.isLcsExist = false;
  452. lineInfo.isEmptyLine = true;
  453. }
  454. lineInfo.lineEndFormat = RC_LINE_FORM::DOS_LINE;
  455. }
  456. else if (lineInfo.unicodeStr.endsWith("\r\n"))
  457. {
  458. if (length > 2)
  459. {
  460. work(lineInfo, 2);
  461. }
  462. else
  463. {
  464. //空白行
  465. lineInfo.isLcsExist = false;
  466. lineInfo.isEmptyLine = true;
  467. }
  468. lineInfo.lineEndFormat = RC_LINE_FORM::DOS_LINE;
  469. }
  470. else if (lineInfo.unicodeStr.endsWith("\n"))
  471. {
  472. if (length > 1)
  473. {
  474. work(lineInfo, 1);
  475. }
  476. else
  477. {
  478. lineInfo.isLcsExist = false;
  479. lineInfo.isEmptyLine = true;
  480. }
  481. lineInfo.lineEndFormat = RC_LINE_FORM::UNIX_LINE;
  482. }
  483. else if (lineInfo.unicodeStr.endsWith("\r"))
  484. {
  485. if (length > 1)
  486. {
  487. work(lineInfo, 1);
  488. }
  489. else
  490. {
  491. lineInfo.isLcsExist = false;
  492. lineInfo.isEmptyLine = true;
  493. }
  494. lineInfo.lineEndFormat = RC_LINE_FORM::MAC_LINE;
  495. }
  496. else
  497. {
  498. if (length > 0)
  499. {
  500. work(lineInfo, 0);
  501. }
  502. else
  503. {
  504. lineInfo.isLcsExist = false;
  505. lineInfo.isEmptyLine = true;
  506. }
  507. lineInfo.lineEndFormat = RC_LINE_FORM::UNKNOWN_LINE;
  508. }
  509. if (lineInfo.isEmptyLine)
  510. {
  511. blankLineInfoVec.append(lineInfo);
  512. }
  513. else
  514. {
  515. lineInfo.md4 = md4.result();
  516. md4.reset();
  517. lineInfoVec.append(lineInfo);
  518. }
  519. ++lineNums;
  520. }
  521. //如果外部指定了格式,则直接返回外部格式
  522. if (fileCode != CODE_ID::UNKOWN)
  523. {
  524. return fileCode;
  525. }
  526. return judgeFinalTextCode(code, isExistUnKownCode, isExistGbk, isExistUtf8);
  527. }
  528. CODE_ID CmpareMode::judgeFinalTextCode(CODE_ID code, bool isExistUnKownCode, bool isExistGbk, bool isExistUtf8)
  529. {
  530. //如果是三种有明确标识的字符编码,则严格按照标识的逻辑去读取。哪怕里面存在错误编码,也只能按照头部标识为准
  531. if (CODE_ID::UNICODE_LE == code || CODE_ID::UNICODE_BE == code || CODE_ID::UTF8_BOM == code || code == CODE_ID::GBK)
  532. {
  533. return code;
  534. }
  535. //剩下的是在文件头没有严格标识编码的文件
  536. //存在不能识别的编码,则应该是ASNI,需要用户指定编码
  537. if (isExistUnKownCode)
  538. {
  539. return CODE_ID::UNKOWN;
  540. }
  541. if (isExistGbk)
  542. {
  543. //如果没有错误码,而且发现gbk,则是gbk编码
  544. return CODE_ID::GBK;
  545. }
  546. //如果不存在错误和gbk,就是纯粹的ut8_nobom
  547. if (isExistUtf8)
  548. {
  549. return CODE_ID::UTF8_NOBOM;
  550. }
  551. return code;
  552. }
  553. //读取用于纯输出,不做比较。bool &isMaybeHexFile 是否是hex文件,不一定准确,做一个推测
  554. // int& charsNums 输出字符个数
  555. CODE_ID CmpareMode::readLineFromFile(uchar* m_fileFpr, const int fileLength, const CODE_ID fileCode, QList<LineFileInfo>&lineInfoVec, int& maxLineSize, int& charsNums, bool &isMaybeHexFile)
  556. {
  557. int lineNums = 0;
  558. CODE_ID code = fileCode;
  559. bool isExistGbk = false;
  560. bool isExistUnKownCode = false;
  561. bool isExistUtf8 = false;
  562. int lineStartPos = 0;
  563. int errorCodeLines = 0;
  564. charsNums = 0;
  565. if (fileCode == CODE_ID::UNICODE_BE || fileCode == CODE_ID::UNICODE_LE)
  566. {
  567. lineStartPos = 2;
  568. }
  569. else if (fileCode == CODE_ID::UTF8_BOM)
  570. {
  571. lineStartPos = 3;
  572. }
  573. //获取一行在文件中
  574. auto getOneLineFromFile = [m_fileFpr](int& startPos, const int fileLength, const CODE_ID fileCode, QByteArray& ret)->bool {
  575. if (startPos < fileLength)
  576. {
  577. ret.clear();
  578. int lineLens = 0;
  579. bool isFindLine = false;
  580. for (int i = startPos; i < fileLength; ++i, ++lineLens)
  581. {
  582. //遇到符号CR
  583. if (m_fileFpr[i] == 0x0D)
  584. {
  585. //后一个是LF,即以CRLF结尾
  586. if ((i + 1 < fileLength) && (m_fileFpr[i + 1] == 0x0A))
  587. {
  588. //lineLens需要加2,因为当前这个没有加,而且后面还有一个\n
  589. ret.append((char*)(m_fileFpr + startPos), lineLens + 2);
  590. startPos += lineLens + 2;
  591. isFindLine = true;
  592. break;
  593. }
  594. else if ((fileCode == UNICODE_BE) && ((i > 0) && m_fileFpr[i - 1] == '\0'))
  595. {
  596. //事先发现就是BE格式,以\0\r\0\n为结尾的
  597. if ((i + 2 < fileLength) && (m_fileFpr[i + 1] == 0x0) && (m_fileFpr[i + 2] == 0x0A))
  598. {
  599. //lineLens需要加3,因为当前这个没有加,而且后面还有一个\0\n
  600. ret.append((char*)(m_fileFpr + startPos), lineLens + 3);
  601. startPos += lineLens + 3;
  602. isFindLine = true;
  603. break;
  604. }
  605. else
  606. {
  607. //虽然说是BE格式,但是后面没有以\0\n结尾,而是以\r结尾。这种多半就是错误。直接按\0\r结尾
  608. //lineLens需要加1,因为当前这个没有加
  609. ret.append((char*)(m_fileFpr + startPos), lineLens + 1);
  610. startPos += lineLens + 1;
  611. isFindLine = true;
  612. break;
  613. }
  614. }
  615. else
  616. {
  617. //直接以\r结尾了,后面没有\n或者\0\n。符合MAC格式,windows可能编码只有\r,没有\n的错误情况。
  618. //lineLens需要加1,因为当前这个没有加
  619. ret.append((char*)(m_fileFpr + startPos), lineLens + 1);
  620. startPos += lineLens + 1;
  621. isFindLine = true;
  622. break;
  623. }
  624. }
  625. else if (m_fileFpr[i] == 0x0A)
  626. {
  627. //没有先遇到\r,直接遇到\n.20210903发现忘记处理该情况le
  628. //lineLens需要加1,因为当前这个没有加
  629. ret.append((char*)(m_fileFpr + startPos), lineLens + 1);
  630. startPos += lineLens + 1;
  631. isFindLine = true;
  632. break;
  633. }
  634. }
  635. //没有找到一行
  636. if (!isFindLine)
  637. {
  638. //最后一行,可能没有带\r\0直接返回
  639. ret.append((char*)(m_fileFpr + startPos), fileLength - startPos);
  640. startPos = fileLength;
  641. }
  642. return true;
  643. }
  644. return false;
  645. };
  646. QByteArray line;
  647. while (getOneLineFromFile(lineStartPos, fileLength, code, line)) {
  648. LineFileInfo lineInfo;
  649. lineInfo.lineNums = lineNums;
  650. /* 这种方式读取文件会包含后面的行尾 */
  651. int length = line.length();
  652. if (maxLineSize < length)
  653. {
  654. maxLineSize = length;
  655. }
  656. //外面必须把code先检测好了
  657. //if (fileCode == CODE_ID::UNICODE_BE /*|| fileCode == CODE_ID::UNICODE_LE */ || fileCode == CODE_ID::UTF8_BOM)
  658. if(fileCode != CODE_ID::UNKOWN)
  659. {
  660. //如果是头部有标识的格式,则后续不用详细检查每行编码,直接按照头部标识走
  661. Encode::tranStrToUNICODE(code, line.data(), line.count(), lineInfo.unicodeStr);
  662. lineInfo.code = fileCode;
  663. }
  664. else if (fileCode == CODE_ID::UNKOWN)
  665. {
  666. /*对于头部没有标识的行,需要每行进行详细检查,比较耗时
  667. *对于第一行已经是GBK的编码,标识出所有的确是GBK的行号
  668. *严格来说,如果以后要做国际版,不应该只考虑GBK,而是要考虑本地ASNI编码。
  669. *对中国而言,本地ASNI编码是GBK,对其它国家,比如日本/韩国而言,这些ASNI是它们本国
  670. *对应的本地编码。
  671. */
  672. recognizeTextCode(line, lineInfo, lineInfo.unicodeStr);
  673. if (CODE_ID::UTF8_NOBOM == lineInfo.code)
  674. {
  675. isExistUtf8 = true;
  676. }
  677. else if (CODE_ID::GBK == lineInfo.code)
  678. {
  679. //如果发现存在GBK,则要以GBK作为字符编码。这里识别gbk是因为显示的时候,需要转化gbk进行显示
  680. isExistGbk = true;
  681. }
  682. else if (CODE_ID::UNKOWN == lineInfo.code)
  683. {
  684. isExistUnKownCode = true;
  685. //增加错误行的计数
  686. errorCodeLines++;
  687. }
  688. }
  689. if (lineInfo.unicodeStr.endsWith("\r\r\n"))
  690. {
  691. //这里是一种错误,但确实可能出现
  692. if (length > 3)
  693. {
  694. }
  695. else
  696. {
  697. //空白行
  698. lineInfo.isLcsExist = false;
  699. lineInfo.isEmptyLine = true;
  700. }
  701. lineInfo.lineEndFormat = RC_LINE_FORM::DOS_LINE;
  702. }
  703. else if (lineInfo.unicodeStr.endsWith("\r\n"))
  704. {
  705. if (length > 2)
  706. {
  707. }
  708. else
  709. {
  710. //空白行
  711. lineInfo.isLcsExist = false;
  712. lineInfo.isEmptyLine = true;
  713. }
  714. lineInfo.lineEndFormat = RC_LINE_FORM::DOS_LINE;
  715. }
  716. else if (lineInfo.unicodeStr.endsWith("\n"))
  717. {
  718. if (length > 1)
  719. {
  720. }
  721. else
  722. {
  723. lineInfo.isLcsExist = false;
  724. lineInfo.isEmptyLine = true;
  725. }
  726. lineInfo.lineEndFormat = RC_LINE_FORM::UNIX_LINE;
  727. }
  728. else if (lineInfo.unicodeStr.endsWith("\r"))
  729. {
  730. if (length > 1)
  731. {
  732. }
  733. else
  734. {
  735. lineInfo.isLcsExist = false;
  736. lineInfo.isEmptyLine = true;
  737. }
  738. lineInfo.lineEndFormat = RC_LINE_FORM::MAC_LINE;
  739. }
  740. else
  741. {
  742. if (length > 0)
  743. {
  744. }
  745. else
  746. {
  747. lineInfo.isLcsExist = false;
  748. lineInfo.isEmptyLine = true;
  749. }
  750. lineInfo.lineEndFormat = RC_LINE_FORM::UNKNOWN_LINE;
  751. }
  752. lineInfoVec.append(lineInfo);
  753. charsNums += lineInfo.unicodeStr.size();
  754. ++lineNums;
  755. }
  756. //如果超过一半的行都是错误的,则考虑为hex文件。
  757. if (lineNums >= 10 && (errorCodeLines * 100 / lineNums > 50))
  758. {
  759. isMaybeHexFile = true;
  760. }
  761. else
  762. {
  763. isMaybeHexFile = false;
  764. //如果前面三行中含有\0字符,也可能是二进制文件
  765. if (lineNums > 3)
  766. {
  767. for (int i = 0; i < 3; ++i)
  768. {
  769. if (lineInfoVec.at(i).unicodeStr.contains(QChar('\0')))
  770. {
  771. isMaybeHexFile = true;
  772. break;
  773. }
  774. }
  775. }
  776. }
  777. //如果用户外部强制编码,则直接按改编码返回
  778. if (fileCode != CODE_ID::UNKOWN)
  779. {
  780. return fileCode;
  781. }
  782. return judgeFinalTextCode(code, isExistUnKownCode, isExistGbk, isExistUtf8);
  783. }
  784. //扫描文件的字符编码,不输出文件
  785. CODE_ID CmpareMode::scanFileRealCode(QString filePath)
  786. {
  787. QFile file(filePath);
  788. file.open(QIODevice::ReadOnly);
  789. CODE_ID code = CODE_ID::UNKOWN;
  790. int lineNums = 0;
  791. bool isExistGbk = false;
  792. bool isExistUnKownCode = false;
  793. bool isExistUtf8 = false;
  794. while (!file.atEnd()) {
  795. LineFileInfo lineInfo;
  796. QByteArray line = file.readLine();
  797. lineInfo.lineNums = lineNums;
  798. /* 这种方式读取文件会包含后面的行尾 */
  799. //int length = line.length();
  800. //第一行时,检测一下文件编码,返回值也是文件的编码
  801. if (0 == lineNums)
  802. {
  803. recognizeTextCode(line, lineInfo, lineInfo.unicodeStr);
  804. code = (CODE_ID)lineInfo.code;
  805. //已经找到文本的标签,相信标签,之前返回
  806. if (code == CODE_ID::UNICODE_BE || code == CODE_ID::UNICODE_LE || code == CODE_ID::UTF8_BOM || code == CODE_ID::GBK)
  807. {
  808. break;
  809. }
  810. }
  811. else
  812. {
  813. /*对于头部没有标识的行,需要每行进行详细检查,比较耗时
  814. *对于第一行已经是GBK的编码,标识出所有的确是GBK的行号
  815. *严格来说,如果以后要做国际版,不应该只考虑GBK,而是要考虑本地ASNI编码。
  816. *对中国而言,本地ASNI编码是GBK,对其它国家,比如日本/韩国而言,这些ASNI是它们本国
  817. *对应的本地编码。
  818. */
  819. recognizeTextCode(line, lineInfo, lineInfo.unicodeStr);
  820. if (CODE_ID::UTF8_NOBOM == lineInfo.code)
  821. {
  822. isExistUtf8 = true;
  823. }
  824. else if (CODE_ID::GBK == lineInfo.code)
  825. {
  826. //如果发现存在GBK,则要以GBK作为字符编码。这里识别gbk是因为显示的时候,需要转化gbk进行显示
  827. isExistGbk = true;
  828. }
  829. else if (CODE_ID::UNKOWN == lineInfo.code)
  830. {
  831. isExistUnKownCode = true;
  832. //20220127一旦发现错误编码,或者说不能识别的编码,则直接跳出。
  833. //因为肯定是不能识别的编码ASNI
  834. break;
  835. }
  836. }
  837. ++lineNums;
  838. if (lineNums >= 1000)
  839. {
  840. break;
  841. }
  842. }
  843. file.close();
  844. return judgeFinalTextCode(code, isExistUnKownCode, isExistGbk, isExistUtf8);
  845. }
  846. //读取文件,并输出
  847. //bytescharsNums:文件字符个数,不是文件大小
  848. //20220908 自动判断是否是二进制文件。isHexFile 是输出
  849. CODE_ID CmpareMode::scanFileOutPut(CODE_ID code, QString filePath, QList<LineFileInfo>& outputLineInfoVec, int &maxLineSize, int& charsNums, bool &isHexFile)
  850. {
  851. QFile* file = new QFile(filePath);
  852. file->open(QIODevice::ReadOnly);
  853. uchar* m_fileFpr = file->map(0, file->size());
  854. if (code == UNKOWN)
  855. {
  856. code = getTextFileEncodeType(m_fileFpr, file->size(), filePath);
  857. }
  858. //UNICODE_LE格式需要单独处理
  859. if (code == UNICODE_LE)
  860. {
  861. readLineFromFileWithUnicodeLe(m_fileFpr, file->size(), outputLineInfoVec, outputLineInfoVec, 0, maxLineSize);
  862. }
  863. else
  864. {
  865. code = readLineFromFile(m_fileFpr, file->size(), code, outputLineInfoVec, maxLineSize, charsNums, isHexFile);
  866. }
  867. file->unmap(m_fileFpr);
  868. file->close();
  869. delete file;
  870. return code;
  871. }