19 #include <QStringList> 20 #include <QTextBoundaryFinder> 21 #include <QRegularExpression> 25 if (
string.isEmpty() )
28 switch ( capitalization )
34 return string.toUpper();
37 return string.toLower();
41 QString temp = string;
43 QTextBoundaryFinder wordSplitter( QTextBoundaryFinder::Word,
string.constData(),
string.length(),
nullptr, 0 );
44 QTextBoundaryFinder letterSplitter( QTextBoundaryFinder::Grapheme,
string.constData(),
string.length(),
nullptr, 0 );
46 wordSplitter.setPosition( 0 );
48 while ( ( first && wordSplitter.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
49 || wordSplitter.toNextBoundary() >= 0 )
52 letterSplitter.setPosition( wordSplitter.position() );
53 letterSplitter.toNextBoundary();
54 QString substr =
string.mid( wordSplitter.position(), letterSplitter.position() - wordSplitter.position() );
55 temp.replace( wordSplitter.position(), substr.length(), substr.toUpper() );
64 static QStringList smallWords;
65 static QStringList newPhraseSeparators;
66 static QRegularExpression splitWords;
67 if ( smallWords.empty() )
69 smallWords = QObject::tr(
"a|an|and|as|at|but|by|en|for|if|in|nor|of|on|or|per|s|the|to|vs.|vs|via" ).split(
'|' );
70 newPhraseSeparators = QObject::tr(
".|:" ).split(
'|' );
71 splitWords = QRegularExpression( QStringLiteral(
"\\b" ), QRegularExpression::UseUnicodePropertiesOption );
74 const QStringList parts =
string.split( splitWords, QString::SkipEmptyParts );
76 bool firstWord =
true;
78 int lastWord = parts.count() - 1;
79 for (
const QString &word : qgis::as_const( parts ) )
81 if ( newPhraseSeparators.contains( word.trimmed() ) )
86 else if ( firstWord || ( i == lastWord ) || !smallWords.contains( word ) )
88 result += word.at( 0 ).toUpper() + word.mid( 1 );
108 for (
int i = 0; i <
string.size(); ++i )
110 QChar ch =
string.at( i );
111 if ( ch.unicode() > 160 )
112 encoded += QStringLiteral(
"&#%1;" ).arg( static_cast< int >( ch.unicode() ) );
113 else if ( ch.unicode() == 38 )
114 encoded += QStringLiteral(
"&" );
115 else if ( ch.unicode() == 60 )
116 encoded += QStringLiteral(
"<" );
117 else if ( ch.unicode() == 62 )
118 encoded += QStringLiteral(
">" );
127 int length1 = string1.length();
128 int length2 = string2.length();
131 if ( string1.isEmpty() )
135 else if ( string2.isEmpty() )
141 QString s1( caseSensitive ? string1 : string1.toLower() );
142 QString s2( caseSensitive ? string2 : string2.toLower() );
144 const QChar *s1Char = s1.constData();
145 const QChar *s2Char = s2.constData();
148 int commonPrefixLen = 0;
149 while ( length1 > 0 && length2 > 0 && *s1Char == *s2Char )
159 while ( length1 > 0 && length2 > 0 && s1.at( commonPrefixLen + length1 - 1 ) == s2.at( commonPrefixLen + length2 - 1 ) )
170 else if ( length2 == 0 )
176 if ( length1 > length2 )
179 std::swap( length1, length2 );
184 col.fill( 0, length2 + 1 );
185 QVector< int > prevCol;
186 prevCol.reserve( length2 + 1 );
187 for (
int i = 0; i < length2 + 1; ++i )
191 const QChar *s2start = s2Char;
192 for (
int i = 0; i < length1; ++i )
196 for (
int j = 0; j < length2; ++j )
198 col[j + 1] = std::min( std::min( 1 + col[j], 1 + prevCol[1 + j] ), prevCol[j] + ( ( *s1Char == *s2Char ) ? 0 : 1 ) );
204 return prevCol[length2];
209 if ( string1.isEmpty() || string2.isEmpty() )
216 QString s1( caseSensitive ? string1 : string1.toLower() );
217 QString s2( caseSensitive ? string2 : string2.toLower() );
225 int *currentScores =
new int [ s2.length()];
226 int *previousScores =
new int [ s2.length()];
227 int maxCommonLength = 0;
228 int lastMaxBeginIndex = 0;
230 const QChar *s1Char = s1.constData();
231 const QChar *s2Char = s2.constData();
232 const QChar *s2Start = s2Char;
234 for (
int i = 0; i < s1.length(); ++i )
236 for (
int j = 0; j < s2.length(); ++j )
238 if ( *s1Char != *s2Char )
240 currentScores[j] = 0;
244 if ( i == 0 || j == 0 )
246 currentScores[j] = 1;
250 currentScores[j] = 1 + previousScores[j - 1];
253 if ( maxCommonLength < currentScores[j] )
255 maxCommonLength = currentScores[j];
256 lastMaxBeginIndex = i;
261 std::swap( currentScores, previousScores );
265 delete [] currentScores;
266 delete [] previousScores;
267 return string1.mid( lastMaxBeginIndex - maxCommonLength + 1, maxCommonLength );
272 if ( string1.isEmpty() && string2.isEmpty() )
278 if ( string1.length() != string2.length() )
285 QString s1( caseSensitive ? string1 : string1.toLower() );
286 QString s2( caseSensitive ? string2 : string2.toLower() );
295 const QChar *s1Char = s1.constData();
296 const QChar *s2Char = s2.constData();
298 for (
int i = 0; i < string1.length(); ++i )
300 if ( *s1Char != *s2Char )
311 if (
string.isEmpty() )
314 QString tmp =
string.toUpper();
317 QChar *char1 = tmp.data();
318 QChar *char2 = tmp.data();
320 for (
int i = 0; i < tmp.length(); ++i, ++char2 )
322 if ( ( *char2 ).unicode() >= 0x41 && ( *char2 ).unicode() <= 0x5A && ( i == 0 || ( ( *char2 ).unicode() != 0x41 && ( *char2 ).unicode() != 0x45
323 && ( *char2 ).unicode() != 0x48 && ( *char2 ).unicode() != 0x49
324 && ( *char2 ).unicode() != 0x4F && ( *char2 ).unicode() != 0x55
325 && ( *char2 ).unicode() != 0x57 && ( *char2 ).unicode() != 0x59 ) ) )
332 tmp.truncate( outLen );
334 QChar *tmpChar = tmp.data();
336 for (
int i = 1; i < tmp.length(); ++i, ++tmpChar )
338 switch ( ( *tmpChar ).unicode() )
344 tmp.replace( i, 1, QChar( 0x31 ) );
355 tmp.replace( i, 1, QChar( 0x32 ) );
360 tmp.replace( i, 1, QChar( 0x33 ) );
364 tmp.replace( i, 1, QChar( 0x34 ) );
369 tmp.replace( i, 1, QChar( 0x35 ) );
373 tmp.replace( i, 1, QChar( 0x36 ) );
383 for (
int i = 1; i < tmp.length(); ++i, ++char2 )
385 if ( *char2 != *char1 )
394 tmp.truncate( outLen );
395 if ( tmp.length() < 4 )
406 QString converted = string;
410 static QRegExp urlRegEx(
"(\\b(([\\w-]+://?|www[.])[^\\s()<>]+(?:\\([\\w\\d]+\\)|([^!\"#$%&'()*+,\\-./:;<=>?@[\\\\\\]^_`{|}~\\s]|/))))" );
411 static QRegExp protoRegEx(
"^(?:f|ht)tps?://" );
412 static QRegExp emailRegEx(
"([\\w._%+-]+@[\\w.-]+\\.[A-Za-z]+)" );
416 while ( urlRegEx.indexIn( converted, offset ) != -1 )
419 QString url = urlRegEx.cap( 1 );
420 QString protoUrl = url;
421 if ( protoRegEx.indexIn( protoUrl ) == -1 )
423 protoUrl.prepend(
"http://" );
425 QString anchor = QStringLiteral(
"<a href=\"%1\">%2</a>" ).arg( protoUrl.toHtmlEscaped(), url.toHtmlEscaped() );
426 converted.replace( urlRegEx.pos( 1 ), url.length(), anchor );
427 offset = urlRegEx.pos( 1 ) + anchor.length();
430 while ( emailRegEx.indexIn( converted, offset ) != -1 )
433 QString email = emailRegEx.cap( 1 );
434 QString anchor = QStringLiteral(
"<a href=\"mailto:%1\">%1</a>" ).arg( email.toHtmlEscaped(), email.toHtmlEscaped() );
435 converted.replace( emailRegEx.pos( 1 ), email.length(), anchor );
436 offset = emailRegEx.pos( 1 ) + anchor.length();
447 , mReplacement( replacement )
448 , mCaseSensitive( caseSensitive )
449 , mWholeWordOnly( wholeWordOnly )
451 if ( mWholeWordOnly )
452 mRx = QRegExp( QString(
"\\b%1\\b" ).arg( mMatch ),
453 mCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive );
458 QString result = input;
459 if ( !mWholeWordOnly )
461 return result.replace( mMatch, mReplacement, mCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive );
465 return result.replace( mRx, mReplacement );
472 map.insert( QStringLiteral(
"match" ), mMatch );
473 map.insert( QStringLiteral(
"replace" ), mReplacement );
474 map.insert( QStringLiteral(
"caseSensitive" ), mCaseSensitive ?
"1" :
"0" );
475 map.insert( QStringLiteral(
"wholeWord" ), mWholeWordOnly ?
"1" :
"0" );
482 properties.value( QStringLiteral(
"replace" ) ),
483 properties.value( QStringLiteral(
"caseSensitive" ), QStringLiteral(
"0" ) ) == QLatin1String(
"1" ),
484 properties.value( QStringLiteral(
"wholeWord" ), QStringLiteral(
"0" ) ) == QLatin1String(
"1" ) );
489 QString result = input;
502 QDomElement propEl = doc.createElement( QStringLiteral(
"replacement" ) );
503 QgsStringMap::const_iterator it = props.constBegin();
504 for ( ; it != props.constEnd(); ++it )
506 propEl.setAttribute( it.key(), it.value() );
508 elem.appendChild( propEl );
514 mReplacements.clear();
515 QDomNodeList nodelist = elem.elementsByTagName( QStringLiteral(
"replacement" ) );
516 for (
int i = 0; i < nodelist.count(); i++ )
518 QDomElement replacementElem = nodelist.at( i ).toElement();
519 QDomNamedNodeMap nodeMap = replacementElem.attributes();
522 for (
int j = 0; j < nodeMap.count(); ++j )
524 props.insert( nodeMap.item( j ).nodeName(), nodeMap.item( j ).nodeValue() );
static QString longestCommonSubstring(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the longest common substring between two strings.
A representation of a single string replacement.
Simple title case conversion - does not fully grammatically parse the text and uses simple rules only...
QMap< QString, QString > QgsStringMap
static QString soundex(const QString &string)
Returns the Soundex representation of a string.
void writeXml(QDomElement &elem, QDomDocument &doc) const
Writes the collection state to an XML element.
static QgsStringReplacement fromProperties(const QgsStringMap &properties)
Creates a new QgsStringReplacement from an encoded properties map.
static QString capitalize(const QString &string, Capitalization capitalization)
Converts a string by applying capitalization rules to the string.
static int levenshteinDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Levenshtein edit distance between two strings.
Convert just the first letter of each word to uppercase, leave the rest untouched.
Convert all characters to uppercase.
Capitalization
Capitalization options.
QgsStringMap properties() const
Returns a map of the replacement properties.
static QString ampersandEncode(const QString &string)
Makes a raw string safe for inclusion as a HTML/XML string literal.
QString process(const QString &input) const
Processes a given input string, applying any valid replacements which should be made.
Mixed case, ie no change.
Convert all characters to lowercase.
void readXml(const QDomElement &elem)
Reads the collection state from an XML element.
QString process(const QString &input) const
Processes a given input string, applying any valid replacements which should be made using QgsStringR...
QgsStringReplacement(const QString &match, const QString &replacement, bool caseSensitive=false, bool wholeWordOnly=false)
Constructor for QgsStringReplacement.
static QString insertLinks(const QString &string, bool *foundLinks=nullptr)
Returns a string with any URL (e.g., http(s)/ftp) and mailto: text converted to valid HTML <a ...
static int hammingDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Hamming distance between two strings.