scan_all_java_encoding.mjs 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. import { readFileSync, readdirSync, statSync } from 'node:fs';
  2. import { join, relative } from 'node:path';
  3. const root = 'd:/work/ylrz_saas/java';
  4. function walk(dir, out = []) {
  5. for (const name of readdirSync(dir)) {
  6. const p = join(dir, name);
  7. const st = statSync(p);
  8. if (st.isDirectory()) {
  9. if (name === 'target' || name === 'node_modules' || name === '.git') continue;
  10. walk(p, out);
  11. } else if (name.endsWith('.java')) {
  12. out.push(p);
  13. }
  14. }
  15. return out;
  16. }
  17. function hasMojibakeLatin(line) {
  18. // UTF-8 CJK misread as Latin-1 often produces 3+ chars in U+00C0..U+00FF range
  19. const m = line.match(/[\u00c0-\u00ff]{3,}/g);
  20. if (!m) return false;
  21. return /\/\/|\/\*|\*|"|'|error\(|success\(|return /.test(line);
  22. }
  23. function scanFile(absPath) {
  24. const raw = readFileSync(absPath);
  25. const rel = relative('d:/work/ylrz_saas', absPath).replace(/\\/g, '/');
  26. const bom = raw.length >= 3 && raw[0] === 0xef && raw[1] === 0xbb && raw[2] === 0xbf;
  27. const t = raw.toString('utf8');
  28. const badLines = [];
  29. t.split('\n').forEach((line, i) => {
  30. const n = i + 1;
  31. if (line.includes('\uFFFD')) {
  32. badLines.push({ line: n, kind: 'FFFD', text: line.trim().slice(0, 100) });
  33. return;
  34. }
  35. if (line.includes('\u951f\u65a4\u70b7')) {
  36. badLines.push({ line: n, kind: 'replacement-seq', text: line.trim().slice(0, 100) });
  37. return;
  38. }
  39. if (hasMojibakeLatin(line)) {
  40. badLines.push({ line: n, kind: 'latin-mojibake', text: line.trim().slice(0, 100) });
  41. return;
  42. }
  43. if (/[^\s"'/\\]\?{2,}[^\s?]/.test(line) && /\/\/|\/\*|"|'|error\(|success\(/.test(line)) {
  44. badLines.push({ line: n, kind: 'qmarks', text: line.trim().slice(0, 100) });
  45. }
  46. });
  47. const unicodeEscapes = (t.match(/\\u[0-9a-fA-F]{4}/g) || []).length;
  48. if (!bom && !badLines.length) return null;
  49. return { rel, bom, badLineCount: badLines.length, badLines: badLines.slice(0, 5), unicodeEscapes };
  50. }
  51. const files = walk(root);
  52. const issues = files.map(scanFile).filter(Boolean);
  53. issues.sort((a, b) => a.rel.localeCompare(b.rel));
  54. const summary = {
  55. scanned: files.length,
  56. filesWithIssues: issues.length,
  57. bomFiles: issues.filter((x) => x.bom).map((x) => x.rel),
  58. fffdFiles: issues.filter((x) => x.badLines.some((l) => l.kind === 'FFFD')).map((x) => x.rel),
  59. issues: issues.map((x) => ({
  60. rel: x.rel,
  61. bom: x.bom,
  62. badLineCount: x.badLineCount,
  63. samples: x.badLines,
  64. })),
  65. };
  66. import { writeFileSync } from 'node:fs';
  67. const outPath = 'd:/work/ylrz_saas/java/fs-service/scripts/scan_java_encoding_result.json';
  68. writeFileSync(outPath, JSON.stringify(summary, null, 2), 'utf8');
  69. console.log('scanned', summary.scanned, 'java files');
  70. console.log('files with issues', summary.filesWithIssues);
  71. console.log('BOM files', summary.bomFiles.length);
  72. console.log('FFFD files', summary.fffdFiles.length);
  73. console.log('result', outPath);
  74. if (summary.fffdFiles.length) {
  75. console.log('FFFD list:');
  76. summary.fffdFiles.forEach((f) => console.log(' ', f));
  77. }