Search code examples
phpparsinggroupingseparatornumberformatter

Looking to see if I misunderstand the numberformatter or whether I am using it incorrectly for parsing and validation


Trying to use the Numberformatter to parse and validate user input for integers (formatter type = DECIMAL). Perhaps it is academic, but I want to be able to change the grouping separator be different than the conventions set in the locale. For example, in the US, the grouping separator ("thousands separator") is by default a comma (','). Changing the grouping separator produces some unexpected results (see unit test code below). I am uncertain whether the formatter simply will not do what I want or whether I am doing it wrong (e.g. should the formatter type = PATTERN_DECIMAL). If the formatter type should be PATTERN_DECIMAL, is there usable documentation somewhere? The ICU documentation is....ummm... not as helpful as it might be.

$this->frmtr = new \NumberFormatter('en-US', NumberFormatter::DECIMAL)         

function testGroupingSeparator() {

    $expectedResult = 12345;
    $this->assertEquals($expectedResult, $this->frmtr->parse("12,345", NumberFormatter::TYPE_INT64));

    // you can change the grouping separator character
    $newChar = '/';
    $this->frmtr->setTextAttribute(NumberFormatter::GROUPING_SEPARATOR_SYMBOL, $newChar);
    $this->assertEquals($newChar, $this->frmtr->getTextAttribute(NumberFormatter::GROUPING_SEPARATOR_SYMBOL));

    // but it appears to have no effect on parsing....the newly set grouping character is treated as 'trailing debris'
    $expectedResult = 12;
    $this->assertEquals($expectedResult, $this->frmtr->parse("12/345", NumberFormatter::TYPE_INT64));

    // semi-colon appears not to work at all as a grouping separator
    $newChar = ';';
    $this->frmtr->setTextAttribute(NumberFormatter::GROUPING_SEPARATOR_SYMBOL, $newChar);
    $this->assertFalse($this->frmtr->parse("12;345", NumberFormatter::TYPE_INT64));

    // it also impacts formatting but in a really odd and unexpected way
    $expectedResult = '12,345.';
    $this->assertEquals($expectedResult, $this->frmtr->format(12345, NumberFormatter::TYPE_INT64));

    // ok, let's set it back to 'normal'
    $this->frmtr->setTextAttribute(NumberFormatter::GROUPING_SEPARATOR_SYMBOL, ',');

    // you can change whether grouping is used
    $this->frmtr->setAttribute(NumberFormatter::GROUPING_USED, false);

    // now the parser will treat the grouping separator as trailing debris which is OK but unexpected perhaps
    $expectedResult = 12;
    $this->assertEquals($expectedResult, $this->frmtr->parse("12,345", NumberFormatter::TYPE_INT64));

    // and the formatting gets screwed up in a weird way with the grouping separator placed at the end
    $expectedResult = '12345,';
    $this->assertEquals($expectedResult, $this->frmtr->format(12345, NumberFormatter::TYPE_INT64));

}

Solution

  • Separators are SYMBOLS, not ATTRIBUTES. See under Predefined constants.

    Use setSymbol instead of setTextAttribute and should get expected results, for instance:

    $frmtr = new \NumberFormatter('en-US', NumberFormatter::DECIMAL);         
    $frmtr->setSymbol(NumberFormatter::GROUPING_SEPARATOR_SYMBOL, '/');
    print $frmtr->format("12345", NumberFormatter::TYPE_INT64) . "\n";
    print $frmtr->parse("12/345", NumberFormatter::TYPE_INT64) . "\n";
    

    Outputs:

    12/345 
    12345