Skip to content

ToR1C1 emits negative offsets with culture NegativeSign (U+2212); ToA1 can't parse them — R1C1 round-trip fails under sv-SE/fi-FI/nb-NO #30

@joakimriedel

Description

@joakimriedel

Summary

FormulaConverter.ToR1C1 serialises negative relative offsets using the current thread culture's NumberFormat.NegativeSign. Under cultures whose negative sign is the Unicode MINUS SIGN U+2212 (sv-SE, fi-FI, nb-NO, …), it emits e.g. RC[−1] using U+2212. The R1C1 reader (ToA1CellFormulaR1C1), however, only accepts the ASCII hyphen-minus - (U+002D), so it can't parse the string the writer just produced — the round-trip throws ParsingException.

Minimal repro (ClosedXML.Parser 2.0.0)

using System.Globalization;
using System.Threading;
using ClosedXML.Parser;

Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("sv-SE"); // NegativeSign = U+2212

// R1C1 form of "=A1/B1" stored in cell C1:
string r1c1 = FormulaConverter.ToR1C1("A1/B1", row: 1, col: 3);
// => "RC[−2]/RC[−1]"   (the offsets use U+2212, not ASCII '-')

string a1 = FormulaConverter.ToA1(r1c1, row: 1, col: 3);
// throws ClosedXML.Parser.ParsingException:
//   The formula `RC[−2]/RC[−1]` wasn't parsed correctly.
//   The expression `RC` was parsed, but the rest `[−2]/RC[−1]` wasn't.

Under en-US / InvariantCulture the same code emits RC[-2]/RC[-1] with ASCII - and round-trips fine.

Current vs expected

  • Current: the R1C1 writer's output is culture-dependent and is not always re-readable by the R1C1 reader.
  • Expected: R1C1 offsets are a serialization/machine format, so the writer should emit the invariant ASCII - regardless of thread culture (and/or the reader should accept U+2212). Either one makes the round-trip culture-independent.

Real-world impact (via ClosedXML 0.105.0)

This surfaces when simply loading a workbook. ClosedXML expands shared formulas at load by converting the master to R1C1 and back to A1 per dependent cell, so loading any .xlsx containing a shared formula with a negative relative reference (e.g. =B2-A2 filled down a column — very common) throws on a thread whose culture uses U+2212:

ClosedXML.Parser.ParsingException: The formula `RC[−2]/RC[−1]` wasn't parsed correctly...
   at ClosedXML.Parser.FormulaConverter.ToA1(String formulaR1C1, Int32 row, Int32 col)
   at ClosedXML.Excel.XLWorkbook.SetCellFormula(...)
   at ClosedXML.Excel.XLWorkbook.LoadCell(...)
   ...
   at ClosedXML.Excel.XLWorkbook..ctor(Stream stream)

Environment

  • ClosedXML.Parser 2.0.0 (didn't spot a related fix on main, though it looks mid-rewrite for 3.0)
  • Reproduced through ClosedXML 0.105.0
  • .NET 10
  • Affected cultures: any whose NumberFormat.NegativeSign is U+2212 — e.g. sv-SE, fi-FI, nb-NO

Happy to open a PR if a fix direction is welcome — my guess is emitting the invariant - in the R1C1 reference writer is the smaller change.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions