Skip to content
Permalink
Browse files

Use QLocale to parse string representation of numbers

Because we now might have thousand separators, also
fixes the NULL issue reported by DelazJ.

Comes with many test cases.
  • Loading branch information
elpaso committed Jun 10, 2018
1 parent cb3eb96 commit 85b9d37fa82b93db2affbf7a205172be1c57ebd1
Showing with 149 additions and 10 deletions.
  1. +62 −10 src/core/qgsfield.cpp
  2. +87 −0 tests/src/core/testqgsfield.cpp
@@ -277,6 +277,68 @@ bool QgsField::convertCompatible( QVariant &v ) const
return false;
}

// Give it a chance to convert to double since for not '.' locales
// we accept both comma and dot as decimal point
if ( d->type == QVariant::Double && v.type() == QVariant::String )
{
QVariant tmp( v );
if ( !tmp.convert( d->type ) )
{
// This might be a string with thousand separator: use locale to convert
bool ok;
double d = QLocale().toDouble( v.toString(), &ok );
if ( ok )
{
v = QVariant( d );
return true;
}
// For not 'dot' locales, we also want to accept '.'
if ( QLocale().decimalPoint() != '.' )
{
d = QLocale( QLocale::English ).toDouble( v.toString(), &ok );
if ( ok )
{
v = QVariant( d );
return true;
}
}
}
}

// For string representation of an int we also might have thousand separator
if ( d->type == QVariant::Int && v.type() == QVariant::String )
{
QVariant tmp( v );
if ( !tmp.convert( d->type ) )
{
// This might be a string with thousand separator: use locale to convert
bool ok;
int i = QLocale().toInt( v.toString(), &ok );
if ( ok )
{
v = QVariant( i );
return true;
}
}
}

// For string representation of a long we also might have thousand separator
if ( d->type == QVariant::LongLong && v.type() == QVariant::String )
{
QVariant tmp( v );
if ( !tmp.convert( d->type ) )
{
// This might be a string with thousand separator: use locale to convert
bool ok;
qlonglong l = QLocale().toLongLong( v.toString(), &ok );
if ( ok )
{
v = QVariant( l );
return true;
}
}
}

//String representations of doubles in QVariant will return false to convert( QVariant::Int )
//work around this by first converting to double, and then checking whether the double is convertible to int
if ( d->type == QVariant::Int && v.canConvert( QVariant::Double ) )
@@ -301,16 +363,6 @@ bool QgsField::convertCompatible( QVariant &v ) const
return true;
}

// Give it a chance to convert to double since for not '.' locales
// we accept both comma and dot as decimal point
if ( d->type == QVariant::Double && QLocale().decimalPoint() != '.' )
{
QVariant tmp( v );
if ( d->type == QVariant::Double && !tmp.convert( d->type ) )
{
v = v.toString().replace( QLocale().decimalPoint(), '.' );
}
}

if ( !v.convert( d->type ) )
{
@@ -498,6 +498,44 @@ void TestQgsField::convertCompatible()
QCOMPARE( longlong.type(), QVariant::LongLong );
QCOMPARE( longlong, QVariant( 99999999999999999LL ) );

//string representation of an int
QVariant stringInt( "123456" );
QVERIFY( intField.convertCompatible( stringInt ) );
QCOMPARE( stringInt.type(), QVariant::Int );
QCOMPARE( stringInt, QVariant( 123456 ) );
// now with group separator for english locale
stringInt = QVariant( "123,456" );
QVERIFY( intField.convertCompatible( stringInt ) );
QCOMPARE( stringInt.type(), QVariant::Int );
QCOMPARE( stringInt, QVariant( "123456" ) );

//string representation of a longlong
QVariant stringLong( "99999999999999999" );
QVERIFY( longlongField.convertCompatible( stringLong ) );
QCOMPARE( stringLong.type(), QVariant::LongLong );
QCOMPARE( stringLong, QVariant( 99999999999999999LL ) );
// now with group separator for english locale
stringLong = QVariant( "99,999,999,999,999,999" );
QVERIFY( longlongField.convertCompatible( stringLong ) );
QCOMPARE( stringLong.type(), QVariant::LongLong );
QCOMPARE( stringLong, QVariant( 99999999999999999LL ) );


//string representation of a double
QVariant stringDouble( "123456.012345" );
QVERIFY( doubleField.convertCompatible( stringDouble ) );
QCOMPARE( stringDouble.type(), QVariant::Double );
QCOMPARE( stringDouble, QVariant( 123456.012345 ) );
// now with group separator for english locale
stringDouble = QVariant( "1,223,456.012345" );
QVERIFY( doubleField.convertCompatible( stringDouble ) );
QCOMPARE( stringDouble.type(), QVariant::Double );
QCOMPARE( stringDouble, QVariant( 1223456.012345 ) );
// This should not convert
stringDouble = QVariant( "1.223.456,012345" );
QVERIFY( ! doubleField.convertCompatible( stringDouble ) );


//double with precision
QgsField doubleWithPrecField( QStringLiteral( "double" ), QVariant::Double, QStringLiteral( "double" ), 10, 3 );
doubleVar = QVariant( 10.12345678 );
@@ -513,12 +551,61 @@ void TestQgsField::convertCompatible()
QCOMPARE( stringVar.type(), QVariant::String );
QCOMPARE( stringVar.toString(), QString( "lon" ) );


/////////////////////////////////////////////////////////
// German locale tests

//double with ',' as decimal separator for German locale
QLocale::setDefault( QLocale::German );
QVariant doubleCommaVar( "1,2345" );
QVERIFY( doubleField.convertCompatible( doubleCommaVar ) );
QCOMPARE( doubleCommaVar.type(), QVariant::Double );
QCOMPARE( doubleCommaVar.toString(), QString( "1.2345" ) );

//string representation of an int
stringInt = QVariant( "123456" );
QVERIFY( intField.convertCompatible( stringInt ) );
QCOMPARE( stringInt.type(), QVariant::Int );
QCOMPARE( stringInt, QVariant( 123456 ) );
// now with group separator for german locale
stringInt = QVariant( "123.456" );
QVERIFY( intField.convertCompatible( stringInt ) );
QCOMPARE( stringInt.type(), QVariant::Int );
QCOMPARE( stringInt, QVariant( "123456" ) );

//string representation of a longlong
stringLong = QVariant( "99999999999999999" );
QVERIFY( longlongField.convertCompatible( stringLong ) );
QCOMPARE( stringLong.type(), QVariant::LongLong );
QCOMPARE( stringLong, QVariant( 99999999999999999LL ) );
// now with group separator for german locale
stringLong = QVariant( "99.999.999.999.999.999" );
QVERIFY( longlongField.convertCompatible( stringLong ) );
QCOMPARE( stringLong.type(), QVariant::LongLong );
QCOMPARE( stringLong, QVariant( 99999999999999999LL ) );

//string representation of a double
stringDouble = QVariant( "123456,012345" );
QVERIFY( doubleField.convertCompatible( stringDouble ) );
QCOMPARE( stringDouble.type(), QVariant::Double );
QCOMPARE( stringDouble, QVariant( 123456.012345 ) );
// For doubles we also want to accept dot as a decimal point
stringDouble = QVariant( "123456.012345" );
QVERIFY( doubleField.convertCompatible( stringDouble ) );
QCOMPARE( stringDouble.type(), QVariant::Double );
QCOMPARE( stringDouble, QVariant( 123456.012345 ) );
// now with group separator for german locale
stringDouble = QVariant( "1.223.456,012345" );
QVERIFY( doubleField.convertCompatible( stringDouble ) );
QCOMPARE( stringDouble.type(), QVariant::Double );
QCOMPARE( stringDouble, QVariant( 1223456.012345 ) );
// Be are good citizens and we also accept english locale
stringDouble = QVariant( "1,223,456.012345" );
QVERIFY( doubleField.convertCompatible( stringDouble ) );
QCOMPARE( stringDouble.type(), QVariant::Double );
QCOMPARE( stringDouble, QVariant( 1223456.012345 ) );


}

void TestQgsField::dataStream()

0 comments on commit 85b9d37

Please sign in to comment.
You can’t perform that action at this time.