92 lines
2.5 KiB
TypeScript
92 lines
2.5 KiB
TypeScript
/**
|
|
* 잘못된 인코딩으로 깨진 한글 문자열 복구 (레포트·전략명 등)
|
|
* — UTF-8 바이트가 Latin-1 으로 해석된 경우(3ë ¶ → 3분, 1시간 → 1시간)
|
|
*/
|
|
|
|
/** Latin-1 바이트 → UTF-8 재해석 */
|
|
function repairFromLatin1Bytes(text: string): string | null {
|
|
try {
|
|
const bytes = Uint8Array.from(text, c => c.charCodeAt(0) & 0xff);
|
|
const repaired = new TextDecoder('utf-8', { fatal: false }).decode(bytes);
|
|
if (!repaired || repaired.includes('\uFFFD')) return null;
|
|
if (/[\uAC00-\uD7A3]/.test(repaired)) return repaired;
|
|
} catch {
|
|
/* ignore */
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/** DB·레거시에서 자주 보이는 깨진 패턴 */
|
|
function repairKnownMojibakePatterns(text: string): string {
|
|
let out = text;
|
|
const rules: [RegExp, string][] = [
|
|
[/CC\s+l(?=\s*[0-9])/gi, 'CCI'],
|
|
[/1시간/gi, '1시간'],
|
|
[/시간/gi, '시간'],
|
|
[/([0-9]+)\s*ë\s*¶/gi, '$1분'],
|
|
[/ë\s*¶/gi, '분'],
|
|
[/시/gi, '시'],
|
|
[/ê°„/gi, '간'],
|
|
[/é/g, 'é'],
|
|
[/Ã /g, ' '],
|
|
];
|
|
for (const [re, rep] of rules) {
|
|
out = out.replace(re, rep);
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function hasHangul(text: string): boolean {
|
|
return /[\uAC00-\uD7A3]/.test(text);
|
|
}
|
|
|
|
function hasMojibakeMarkers(text: string): boolean {
|
|
return /[ìíîïðñòóôõö÷øùúûüÿàáâãäåæçèêë]/.test(text)
|
|
|| /ë\s*¶/.test(text)
|
|
|| /시/.test(text)
|
|
|| /ê°„/.test(text)
|
|
|| /Ã.|Â./.test(text);
|
|
}
|
|
|
|
function looksLikeBrokenKorean(text: string): boolean {
|
|
if (!text) return false;
|
|
if (hasHangul(text) && hasMojibakeMarkers(text)) return true;
|
|
if (hasMojibakeMarkers(text) && !hasHangul(text)) return true;
|
|
return false;
|
|
}
|
|
|
|
function normalizeReportText(text: string): string {
|
|
return text
|
|
.replace(/,{2,}/g, ', ')
|
|
.replace(/\s*,\s*,+/g, ', ')
|
|
.replace(/,\s*,/g, ', ')
|
|
.replace(/\s{2,}/g, ' ')
|
|
.trim();
|
|
}
|
|
|
|
/**
|
|
* 깨진 한글·시간봉 표기 복구. 이미 정상 UTF-8 이면 그대로 반환.
|
|
*/
|
|
export function repairUtf8Mojibake(text: string): string {
|
|
if (!text) return text;
|
|
|
|
let result = text;
|
|
|
|
if (hasMojibakeMarkers(result)) {
|
|
result = repairKnownMojibakePatterns(result);
|
|
}
|
|
|
|
if (looksLikeBrokenKorean(text)) {
|
|
const fromBytes = repairFromLatin1Bytes(text);
|
|
if (fromBytes && hasHangul(fromBytes) && !looksLikeBrokenKorean(fromBytes)) {
|
|
result = fromBytes;
|
|
}
|
|
}
|
|
|
|
if (hasMojibakeMarkers(result)) {
|
|
result = repairKnownMojibakePatterns(result);
|
|
}
|
|
|
|
return normalizeReportText(result);
|
|
}
|