Skip to content

Commit 0fbcc4b

Browse files
nirvnnyalldawson
authored andcommittedNov 7, 2016
[FEATURE] upgrade the substr() function
- support negative start value (e.g. substr('hello',-2) returns 'lo') - support negative length value (e.g. substr('hello',3,-1) returns 'll') - length parameter now optional, defaults to end of string (e.g. substr('hello world',7) returns 'world')
1 parent 11405af commit 0fbcc4b

File tree

3 files changed

+63
-11
lines changed

3 files changed

+63
-11
lines changed
 

‎resources/function_help/json/substr

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,15 @@
22
"name": "substr",
33
"type": "function",
44
"description": "Returns a part of a string.",
5-
"arguments": [ {"arg":"input_string","description":"the full input string"},
6-
{"arg":"startpos","description":"integer representing start position to extract from"},
7-
{"arg":"length","description":"integer representing length of string to extract"}
5+
"arguments": [ {"arg":"string","description":"the full input string"},
6+
{"arg":"start","description":"integer representing start position to extract from; if start is negative, the return string will begin at the end of the string minus the start value"},
7+
{"arg":"length","optional":true,"description":"integer representing length of string to extract; if length is negative, the return string will omit the given length of characters from the end of the string"}
88
],
9-
"examples": [ { "expression":"substr('HELLO WORLD',3,5)", "returns":"'LLO W'"}]
9+
"examples": [ { "expression":"substr('HELLO WORLD',3,5)", "returns":"'LLO W'"},
10+
{ "expression":"substr('HELLO WORLD',6)", "returns":"'WORLD'"},
11+
{ "expression":"substr('HELLO WORLD',-5)","returns":"'WORLD'"},
12+
{ "expression":"substr('HELLO',3,-1)", "returns":"'LL'"},
13+
{ "expression":"substr('HELLO WORLD',-5,2)","returns":"'WO'"},
14+
{ "expression":"substr('HELLO WORLD',-5,-1)","returns":"'WORL'"}
15+
]
1016
}

‎src/core/qgsexpression.cpp

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,12 +1398,55 @@ static QVariant fcnUuid( const QVariantList&, const QgsExpressionContext*, QgsEx
13981398
return QUuid::createUuid().toString();
13991399
}
14001400

1401-
static QVariant fcnSubstr( const QVariantList& values, const QgsExpressionContext*, QgsExpression* parent )
1401+
static QVariant fcnSubstr( const QVariantList& values, const QgsExpressionContext* context, QgsExpression* parent )
14021402
{
1403-
QString str = getStringValue( values.at( 0 ), parent );
1404-
int from = getIntValue( values.at( 1 ), parent );
1405-
int len = getIntValue( values.at( 2 ), parent );
1406-
return QVariant( str.mid( from -1, len ) );
1403+
1404+
QgsExpression::Node* node;
1405+
1406+
node = getNode( values.at( 0 ), parent );
1407+
ENSURE_NO_EVAL_ERROR;
1408+
QString str = node->eval( parent, context ).toString();
1409+
1410+
node = getNode( values.at( 1 ), parent );
1411+
ENSURE_NO_EVAL_ERROR;
1412+
int from = node->eval( parent, context ).toInt();
1413+
1414+
node = getNode( values.at( 2 ), parent );
1415+
QVariant value = node->eval( parent, context );
1416+
int len;
1417+
if ( value.isValid() )
1418+
{
1419+
len = value.toInt();
1420+
}
1421+
else
1422+
{
1423+
len = str.size();
1424+
}
1425+
1426+
if ( from < 0 )
1427+
{
1428+
from = str.size() + from;
1429+
if ( from < 0 )
1430+
{
1431+
from = 0;
1432+
}
1433+
}
1434+
else if ( from > 0 )
1435+
{
1436+
//account for the fact that substr() starts at 1
1437+
from -= 1;
1438+
}
1439+
1440+
if ( len < 0 )
1441+
{
1442+
len = str.size() + len - from;
1443+
if ( len < 0 )
1444+
{
1445+
len = 0;
1446+
}
1447+
}
1448+
1449+
return QVariant( str.mid( from, len ) );
14071450
}
14081451
static QVariant fcnFeatureId( const QVariantList&, const QgsExpressionContext* context, QgsExpression* )
14091452
{
@@ -3804,7 +3847,7 @@ const QList<QgsExpression::Function*>& QgsExpression::Functions()
38043847
<< new StaticFunction( QStringLiteral( "replace" ), -1, fcnReplace, QStringLiteral( "String" ) )
38053848
<< new StaticFunction( QStringLiteral( "regexp_replace" ), 3, fcnRegexpReplace, QStringLiteral( "String" ) )
38063849
<< new StaticFunction( QStringLiteral( "regexp_substr" ), 2, fcnRegexpSubstr, QStringLiteral( "String" ) )
3807-
<< new StaticFunction( QStringLiteral( "substr" ), 3, fcnSubstr, QStringLiteral( "String" ) )
3850+
<< new StaticFunction( QStringLiteral( "substr" ), ParameterList() << Parameter( QStringLiteral( "string" ) ) << Parameter( QStringLiteral( "start " ) ) << Parameter( QStringLiteral( "length" ), true ), fcnSubstr, QStringLiteral( "String" ), QString(), False, QSet<QString>(), true )
38083851
<< new StaticFunction( QStringLiteral( "concat" ), -1, fcnConcat, QStringLiteral( "String" ), QString(), false, QSet<QString>(), false, QStringList(), true )
38093852
<< new StaticFunction( QStringLiteral( "strpos" ), 2, fcnStrpos, QStringLiteral( "String" ) )
38103853
<< new StaticFunction( QStringLiteral( "left" ), 2, fcnLeft, QStringLiteral( "String" ) )

‎tests/src/core/testqgsexpression.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -836,7 +836,10 @@ class TestQgsExpression: public QObject
836836
QTest::newRow( "regexp_replace cap group" ) << "regexp_replace('HeLLo','(eL)', 'x\\\\1x')" << false << QVariant( "HxeLxLo" );
837837
QTest::newRow( "regexp_replace invalid" ) << "regexp_replace('HeLLo','[[[', '-')" << true << QVariant();
838838
QTest::newRow( "substr" ) << "substr('HeLLo', 3,2)" << false << QVariant( "LL" );
839-
QTest::newRow( "substr outside" ) << "substr('HeLLo', -5,2)" << false << QVariant( "" );
839+
QTest::newRow( "substr negative start" ) << "substr('HeLLo', -4)" << false << QVariant( "eLLo" );
840+
QTest::newRow( "substr negative length" ) << "substr('HeLLo', 1,-3)" << false << QVariant( "He" );
841+
QTest::newRow( "substr positive start and negative length" ) << "substr('HeLLo', 3,-1)" << false << QVariant( "LL" );
842+
QTest::newRow( "substr start only" ) << "substr('HeLLo', 3)" << false << QVariant( "LLo" );
840843
QTest::newRow( "regexp_substr" ) << "regexp_substr('abc123','(\\\\d+)')" << false << QVariant( "123" );
841844
QTest::newRow( "regexp_substr non-greedy" ) << "regexp_substr('abc123','(\\\\d+?)')" << false << QVariant( "1" );
842845
QTest::newRow( "regexp_substr no hit" ) << "regexp_substr('abcdef','(\\\\d+)')" << false << QVariant( "" );

0 commit comments

Comments
 (0)
Please sign in to comment.