SSteven
SSteven

Reputation: 753

Does money_get<> work correctly?

On coliru, I have tested the money_get<> facet for USD and EUR.

It works correctly (as far as my test data was concerned) for USD, for not for EUR.

The program to test it for USD is:

int main()
{
    std::string str = "$1.11 $2.22 $3.33 4.44 5.55";

    std::istringstream s1(str);
    s1.imbue(std::locale("en_US.UTF-8"));

    std::cout << std::fixed << std::setprecision(2);
    std::cout << '"' << str << "\" parsed with the I/O manipulator: ";

    long double val;

    while(s1 >> std::get_money(val))
        std::cout << val/100 << ' ';
    std::cout << '\n';


    str = "USD  1,234.56";

    std::istringstream s2(str);
    s2.imbue(std::locale("en_US.UTF-8"));

    std::cout << '"' << str << "\" parsed with the facet directly: ";

    auto& f = std::use_facet<std::money_get<char>>(s2.getloc());
    std::ios_base::iostate err;
    std::istreambuf_iterator<char> beg(s2), end;

    f.get(beg, end, true, s2, err, val);

    std::cout << val/100 << '\n';
}

http://coliru.stacked-crooked.com/a/be545f641718040e

The O/P is correct:

"$1.11 $2.22 $3.33 4.44 5.55" parsed with the I/O manipulator: 1.11 2.22 3.33 4.44 5.55 
"USD  1,234.56" parsed with the facet directly: 1234.56

The program to test it for EUR is similar:

int main()
{
    std::string str = "1,11 2,22 3,33 € 456789,12 €";

    std::istringstream s1(str);
    s1.imbue(std::locale("de_DE.utf8"));

    std::cout << std::fixed << std::setprecision(2);
    std::cout << '"' << str << "\" parsed with the I/O manipulator: ";

    long double val;

    while(s1 >> std::get_money(val))
        std::cout << val/100 << ' ';
    std::cout << '\n';


    str = "1.234,56 EUR";

    std::istringstream s2(str);
    s2.imbue(std::locale("de_DE.utf8"));

    std::cout << '"' << str << "\" parsed with the facet directly: ";

    auto& f = std::use_facet<std::money_get<char>>(s2.getloc());
    std::ios_base::iostate err;
    std::istreambuf_iterator<char> beg(s2), end;

    f.get(beg, end, true, s2, err, val);

    std::cout << val/100 << '\n';
}

http://coliru.stacked-crooked.com/a/a91720a9dbd4eb5e

The O/P is wrong:

"1,11 2,22 3,33 € 456789,12 €" parsed with the I/O manipulator: 1.11 2.22 3.33 
"1.234,56 EUR" parsed with the facet directly: 1234.56

Notice that the 4th Euro amount is not printed. This is because as soon as the money_get<>.get() function encounters the € symbol in the 3rd value, it gives an error. It processes only plain amounts without the € symbol.

The money_punct<> facet on coliru for the German locale gives the following definitions:

moneypunct in locale "de_DE.utf8":
 decimal_point: ,
 thousands_sep: .
 grouping:      3 3 
 curr_symbol:   €
 positive_sign: 
 negative_sign: -
 frac_digits:   2
 pos_format:    sign value space symbol 
 neg_format:    sign value space symbol 

Notice that I had entered the Euro amounts correctly as specified by neg_format.

How can such a problem be solved?

Thanks.

Upvotes: 0

Views: 201

Answers (2)

SSteven
SSteven

Reputation: 753

Yes, you're right, Cubbi.

Based on the info provided by you, I've written the programs and I have explanations for why this happens.

1) The program for reading USD amounts was already working. No problem there.

http://coliru.stacked-crooked.com/a/be545f641718040e

2) To read EUR amounts, you have to set the input stream's showbase flag, as indicated by Cubbi.

When the EUR amount involved uses the local symbol €, that's all that's necessary.

However, when the EUR amount involved uses the international symbol EUR, the symbol in the read data must be a 4-character string "EUR ". The last space is important, else it won't be consumed. This may be because the international representation is a 4-character C-style string, with the last character being the terminating zero. [Stroustrup, "The C++ Programming Language", 4th Ed, pg 1136.]

http://coliru.stacked-crooked.com/a/ef38fb1d1c98aa22

/// ...

str = "1.234,56 EUR ";

std::istringstream s2(str);
s2.imbue(std::locale("de_DE.utf8"));
// s2 >> std::showbase;

std::cout << '"' << str << "\" parsed with the facet directly: ";

auto& f = std::use_facet<std::money_get<char>>(s2.getloc());
std::ios_base::iostate err;
std::istreambuf_iterator<char> beg(s2), end;

f.get(beg, end, true, s2, err, val);

std::cout << val/100 << '\n';


"1.234,56 EUR " parsed with the facet directly: 1234.56  

3) Similarly, to read INR amounts, you have to set the input stream's showbase flag.

When the INR amount involved uses the local symbol ₹, that's all that's necessary.

However, when the INR amount involved uses the international symbol INR, the symbol in the read data must be a 4-character string "INR ". The last space is important, else it won't be consumed.

http://coliru.stacked-crooked.com/a/216cc78592141ae1

/// ...

str = "INR  100,234.56";

std::istringstream s2(str);
s2.imbue(std::locale("en_IN"));

//  s2 >> std::showbase;

std::cout << '"' << str << "\" parsed with the facet directly: ";

auto& f = std::use_facet<std::money_get<char>>(s2.getloc());
std::ios_base::iostate err;
std::istreambuf_iterator<char> beg(s2), end;

f.get(beg, end, true, s2, err, val);

std::cout << val/100 << '\n';

Notice there are 2 adjacent spaces in the data string: 1 after the INR symbol and 1 before the value. This is required by the pattern {sign symbol space value}.

"INR  100,234.56" parsed with the facet directly: 100234.56

Upvotes: 0

Cubbi
Cubbi

Reputation: 47468

As mentioned on cppreference page for std::money_get::get, std::showbase is necessary to make the currency symbol in the end position non-optional:

std::string str = "3,33 € 456789,12 €";
std::istringstream s1(str);
s1.imbue(std::locale("de_DE.utf8"));
std::cout << std::fixed << std::setprecision(2);
std::cout << '"' << str << "\" parsed with the I/O manipulator: ";
long double val;
s1 >> std::showbase; // <-- this
while(s1 >> std::get_money(val))
    std::cout << val/100 << ' ';
std::cout << '\n';

output on coliru

"3,33 € 456789,12 €" parsed with the I/O manipulator: 3.33 456789.12 

Upvotes: 1

Related Questions