Error while parsing USD and $ amounts using money_get<>

I am getting an error while parsing USD and $ amounts using money_get<>.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    #include <iostream>         /// cin, cout
    #include <locale>
    #include <iterator>
    
    using namespace std;
    
    
    int main()
    {
        cin.imbue(std::locale("en_US.UTF-8"));
    
        std::cout << "USD  1.11$2.22"
                  << " parsed with the facet directly: ";
    
        auto& f = std::use_facet<std::money_get<char>>(cin.getloc());
        std::ios_base::iostate err;
        std::istreambuf_iterator<char> frm(cin), end;
        long double val;
    
        /// international currency symbol
        frm = f.get(frm, end, true, cin, err, val);
        std::cout << "\n "<< val/100;
        
        string str;
        cin >> str;
        cout << "\n remaining: " << str << endl;
    
        /// local currency symbol
        f.get(frm, end, false, cin, err, val);
        std::cout << "\n "<< val/100;
    }    


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


The input is:

    USD  1.11
    $2.22



The output is:

    USD  1.11$2.22 parsed with the facet directly: 
     1.11
     remaining: .22
     0


The patterns from the corresponding moneypunct<> facet are:
for USD: sign symbol space value
for $: sign symbol value none

Obviously, I'm overshooting the mark when the 1st amount is read. Why is this happening? Thanks.
Above I was trying to read a combination of USD amounts and $ amounts using money_get<>.

I have isolated the problem further. In fact reading USD amounts using money_get<> works fine, but reading $ amounts using money_get<> results in a read error. (Reading $ amounts using the get_money() manipulator works fine.)

USD amounts are OK: http://coliru.stacked-crooked.com/a/efac99c23f406da6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>         /// cin, cout
#include <locale>
#include <iterator>

using namespace std;


int main()
{
    cin.imbue(std::locale("en_US.UTF-8"));

    std::cout << "USD  1.11USD  2.22USD  3.33USD  1,064.59"
              << " parsed with the facet directly: ";

    auto& f = std::use_facet<std::money_get<char>>(cin.getloc());

    std::istreambuf_iterator<char> frm(cin), end;
    std::ios_base::iostate err;
    long double val;

    while ( (frm = f.get(frm, end, true, cin, err, val)) != end )
       std::cout << "\n " << val/100;
}

Output:

USD  1.11USD  2.22USD  3.33USD  1,064.59 parsed with the facet directly: 
 1.11
 2.22
 3.33
 1064.59



$ amounts result in an error: http://coliru.stacked-crooked.com/a/57dd859be528d5de
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>         /// cin, cout
#include <locale>
#include <iterator>

using namespace std;


int main()
{
    cin.imbue(std::locale("en_US.UTF-8"));

    std::cout << "$1.11$2.22$3.33$1,064.59"
              << " parsed with the facet directly: ";

    auto& f = std::use_facet<std::money_get<char>>(cin.getloc());

    std::istreambuf_iterator<char> frm(cin), end;
    std::ios_base::iostate err;
    long double val;

    while ( (frm = f.get(frm, end, false, cin, err, val)) != end )
       std::cout << "\n " << val/100;
}

Output:

$1.11$2.22$3.33$1,064.59 parsed with the facet directly: 
 0.11
 0.22
 0.33
 0
 0
 ... infinite loop till timeout


Any idea what the problem is? Thanks.
This gets even more interesting.

I have finally isolated the problem even further.

It doesn't seem to be a problem of the get_money() manipulator or of the money_get<> facet.

It is an issue of string v/s cin - Input of monetary amounts specified using local currency(eg: $) works correctly from an istringstream but not from cin.

http://coliru.stacked-crooked.com/a/19e0fdc663c5d845

Input of monetary amounts specified using local currency(eg: $) works correctly from an istringstream:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
string str = "$1.11 $2.22 $3.33 4.44 5.55";


void stringInput()          /// works
{
    cout << "string Input ... works:\n";
    
    istringstream s1(str);
    s1.imbue(locale("en_US.UTF-8"));

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

    while(s1 >> get_money(val))
        cout << val/100 << ' ';

    cout << "\n";
}

Output:

string Input ... works:
"$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 



Input of monetary amounts specified using local currency(eg: $) works incorrectly from cin (same link):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void cinInput()             /// does not work
{
    cout << "cin Input ... doesn't work:\n";
    
    cin.imbue(locale("en_US.UTF-8"));

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

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

Output:

cin Input ... doesn't work:
"$1.11 $2.22 $3.33 4.44 5.55" parsed with the I/O manipulator: 0.11 0.22 0.33 0.44 0.55 


Please note that I have modified some of the above code from cppreference.com
OK, I've finally precisely pinpointed the problem and solved it.

The root cause of the problem is not a C++ issue at all. cin, istringstream, get_money() and money_get<> all work just fine.

It's basically an input issue.

Consider the following input string:


$1.11 $2.22 $3.33 $4.44 $5.55


When entered offline, it is read perfectly correctly, as entered.

However, when entered online, the "$1", "$2", "$3", etc. are not read into the input. They are treated as special characters. This is why on coliru (and possibly any other online C++ engine), the $ amounts were not getting read correctly. This may be a UNIX issue, where $ is treated as a special character.

The solution is to escape it: instead of $, enter \$.


\$1.11 \$2.22 \$3.33 \$4.44 \$5.55


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

There's some junk on that link, but you can get the gist of it.
-- EDIT --
The link only pinpoints the root cause. I shall use it to develop the complete solution and post it later.
Last edited on
And here's a working solution: http://coliru.stacked-crooked.com/a/14287a7a0e05c32a

Reading local currency amounts from cin:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/// Read local currency amounts
void readLclCur(const string& smoney)
{
    cout << "money amounts: " << smoney << endl;

    istringstream ssmoney {smoney};
    ssmoney.imbue(locUS);
    ssmoney >> showbase;                /// optional

    cout << fixed << setprecision(2);
    cout << "amounts parsed with the I/O manipulator: ";

    long double val;

    while(ssmoney >> get_money(val))
        cout << val/100 << ' ';

    cout << endl << endl;
}

cin content:

\$1.11 \$2.22 \$3.33 \$4.44 \$5.55 \$1,034.68

Output:

amounts parsed with the I/O manipulator: 1.11 2.22 3.33 4.44 5.55 1034.68 



Reading international currency amounts from cin:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/// Read international currency amounts
void readIntCur(const string& smoney)
{
    cout << "money amounts: " << smoney << endl;

    istringstream ssmoney {smoney};
    ssmoney.imbue(locUS);
    ssmoney >> showbase;                /// optional

    cout << fixed << setprecision(2);
    cout << "amounts parsed with the facet directly: ";

    auto& f = std::use_facet<std::money_get<char>>(ssmoney.getloc());

    std::istreambuf_iterator<char> frm(ssmoney), end;
    std::ios_base::iostate err;
    long double val;

    while ( (frm = f.get(frm, end, true, ssmoney, err, val)) != end )
    {
       std::cout << val/100 << " " ;
    }

    /// final value
    std::cout << val/100 << " " ;
   
    cout << endl << endl;
}

cin content:

USD  1.11USD  2.22USD  3.33USD  4.44USD  5.55USD  2096.38

Output:

amounts parsed with the facet directly: 1.11 2.22 3.33 4.44 5.55 2096.38


In the readIntCur() function, please note the extra cout statement at the end for the final value. This is needed since we are checking for "end" at the start of the loop and it will be returned after reading the final value; hence wouldn't be printed. Therefore an extra cout is needed.

Can that be handled better? I was thinking about a do-while loop, but if there is no I/P, a 0 would be printed.
There is a better way to terminate the loop when reading money amounts using the money_get<> facet. It is more concise:

1
2
3
4
5
6
    do
    {
       f.get(frm, end, true, ssmoney, err, val);
       
       std::cout << val/100 << " " ;
    } while (!err);


http://coliru.stacked-crooked.com/a/453d795852fe6da0

This always does the following:
1) Gets an amount (even if there is none).
2) Prints it.

At the end of the loop, it checks if there was an error. If not, it continues with the next iteration. If there was an error, it exits.

The problem is that even if there is no amount in the input, it prints 0. Is there a way to avoid this?

Thanks.
Registered users can post here. Sign in or register to post.