fix(spreadsheet): 형식 끝·구역 나눔 앞 홀로 남은 역슬래시가 ₩ 로 보이던 것 고침

브레인이 증거로 찾은 rv.json(사용자 붙여넣기 저장분)의 형식 문자열이 끝에
역슬래시 하나만 남아(`_-* #,##0.00_-\` 등) 있었음 — 엑셀 mso-number-format
의 `\;`(구역 나눔 이스케이프) 자르기가 깨진 자투리. numfmt 가 이 홀로 남은
역슬래시를 글자로 찍었고 굴림 등 한글 글꼴은 역슬래시(0x5C)를 ₩ 모양으로
그려 사용자 화면에 ₩ 가 붙어 보였음. 세 자리(splitSections · tokenize ·
detectDateFormat)에서 ① 형식 끝에 다음 글자가 없는 역슬래시 ② 구역 나눔
`;` 바로 앞 역슬래시를 모두 버림(뒤 `;` 는 그대로 나눔으로 처리)으로 고침.
이미 저장된 rv.json 의 13 곳도 자투리 역슬래시를 지움. 사용자 견본값
2.20 · 1.70 · -6.82 로 회귀 시험 세 건 더함 — 시험 196건 전부 통과.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RrQ65VtGZbae2VKgYVMhmc
This commit is contained in:
2026-09-28 13:39:50 +09:00
co-authored by Claude Sonnet 5
parent b98fbccdd0
commit 41a717f62b
3 changed files with 39 additions and 18 deletions
+9 -5
View File
@@ -44,7 +44,10 @@ function splitSections(code: string): string[] {
cur += ch;
continue;
}
if (ch === "\\" && i + 1 < code.length) {
if (ch === "\\") {
// 형식 끝에 홀로 남은 역슬래시 · 구역 나눔(`;`) 바로 앞 역슬래시는 깨진 자투리로 보고 버림
// (엑셀 붙여넣기가 잘려 남긴 것 — 2026-09-28 실측 rv.json). 그 밖엔 원래대로 다음 글자를 이스케이프.
if (i + 1 >= code.length || code[i + 1] === ";") continue;
cur += ch + code[i + 1];
i++;
continue;
@@ -108,8 +111,9 @@ function tokenize(s: string): { toks: Tok[]; fillChar?: string } {
i = j;
continue;
}
if (ch === "\\" && i + 1 < s.length) {
toks.push({ k: "lit", ch: s[i + 1] });
if (ch === "\\") {
// 홀로 남은 역슬래시(뒤에 이스케이프할 글자가 없음)는 굴림 등 글꼴에서 ₩ 모양으로 보여 버림.
if (i + 1 < s.length) toks.push({ k: "lit", ch: s[i + 1] });
i++;
continue;
}
@@ -449,8 +453,8 @@ function detectDateFormat(s: string): DatePart[] | null {
i = j + 1;
continue;
}
if (ch === "\\" && i + 1 < s.length) {
parts.push({ k: "lit", text: s[i + 1] });
if (ch === "\\") {
if (i + 1 < s.length) parts.push({ k: "lit", text: s[i + 1] });
i += 2;
continue;
}
+13 -13
View File
@@ -52,7 +52,7 @@
},
{
"가로": "center",
"형식": "_ * #,##0.00_ \\",
"형식": "_ * #,##0.00_ ",
"테두리": {
"위": {
"선": "hair"
@@ -61,7 +61,7 @@
},
{
"가로": "center",
"형식": "_-* #,##0.00_-\\",
"형식": "_-* #,##0.00_-",
"테두리": {
"위": {
"선": "hair"
@@ -86,7 +86,7 @@
},
{
"가로": "center",
"형식": "0.00_)\\",
"형식": "0.00_)",
"테두리": {
"위": {
"선": "hair"
@@ -132,7 +132,7 @@
},
{
"가로": "center",
"형식": "_-* #,##0_-\\",
"형식": "_-* #,##0_-",
"테두리": {
"위": {
"선": "hair"
@@ -144,7 +144,7 @@
},
{
"가로": "center",
"형식": "_-* #,##0.0_-\\",
"형식": "_-* #,##0.0_-",
"테두리": {
"위": {
"선": "hair"
@@ -168,7 +168,7 @@
},
{
"가로": "center",
"형식": "_-* #,##0.00_-\\",
"형식": "_-* #,##0.00_-",
"테두리": {
"위": {
"선": "hair"
@@ -179,7 +179,7 @@
}
},
{
"형식": "_-* #,##0.00_-\\",
"형식": "_-* #,##0.00_-",
"테두리": {
"위": {
"선": "hair"
@@ -224,7 +224,7 @@
},
{
"가로": "center",
"형식": "0.00_)\\",
"형식": "0.00_)",
"테두리": {
"위": {
"선": "hair"
@@ -282,7 +282,7 @@
},
{
"가로": "center",
"형식": "_ * #,##0.00_ \\",
"형식": "_ * #,##0.00_ ",
"테두리": {
"아래": {
"선": "hair"
@@ -298,7 +298,7 @@
},
{
"가로": "center",
"형식": "0.00_)\\",
"형식": "0.00_)",
"테두리": {
"아래": {
"선": "hair"
@@ -414,11 +414,11 @@
},
{
"가로": "center",
"형식": "_ * #,##0.00_ \\"
"형식": "_ * #,##0.00_ "
},
{
"가로": "center",
"형식": "_-* #,##0.00_-\\"
"형식": "_-* #,##0.00_-"
},
{
"테두리": {
@@ -495,7 +495,7 @@
},
{
"가로": "center",
"형식": "0.00_)\\",
"형식": "0.00_)",
"테두리": {
"아래": {
"선": "thin"
@@ -545,6 +545,23 @@ ok(
fmt(frac(1234567n), "#,##0") === "1,234,567",
fmt(frac(1234567n), "#,##0"),
);
ok(
"형식 끝 홀로 남은 역슬래시는 ₩ 로 안 보임(rv.json 실측)",
!fmt(toFrac("2.20"), "_-* #,##0.00_-\\").includes("₩") &&
fmt(toFrac("2.20"), "_-* #,##0.00_-\\") === " 2.20 ",
fmt(toFrac("2.20"), "_-* #,##0.00_-\\"),
);
ok(
"형식 끝 홀로 남은 역슬래시(0.00_) 꼴)도 ₩ 로 안 보임",
!fmt(toFrac("1.70"), "0.00_)\\").includes("₩") && fmt(toFrac("1.70"), "0.00_)\\") === "1.70 ",
fmt(toFrac("1.70"), "0.00_)\\"),
);
ok(
"구역 나눔 앞 역슬래시도 ₩ 로 안 남고 나눔이 그대로 일어남",
!fmt(toFrac("-6.82"), "0.00_-\\;-0.00_-").includes("₩") &&
fmt(toFrac("-6.82"), "0.00_-\\;-0.00_-") === "-6.82 ",
fmt(toFrac("-6.82"), "0.00_-\\;-0.00_-"),
);
// ── 채우기 ──────────────────────────────────────────────────────────────