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 );
102 result.remove(
' ' );
113 for (
int i = 0; i <
string.size(); ++i )
115 QChar ch =
string.at( i );
116 if ( ch.unicode() > 160 )
117 encoded += QStringLiteral(
"&#%1;" ).arg( static_cast< int >( ch.unicode() ) );
118 else if ( ch.unicode() == 38 )
119 encoded += QStringLiteral(
"&" );
120 else if ( ch.unicode() == 60 )
121 encoded += QStringLiteral(
"<" );
122 else if ( ch.unicode() == 62 )
123 encoded += QStringLiteral(
">" );
132 int length1 = string1.length();
133 int length2 = string2.length();
136 if ( string1.isEmpty() )
140 else if ( string2.isEmpty() )
146 QString s1( caseSensitive ? string1 : string1.toLower() );
147 QString s2( caseSensitive ? string2 : string2.toLower() );
149 const QChar *s1Char = s1.constData();
150 const QChar *s2Char = s2.constData();
153 int commonPrefixLen = 0;
154 while ( length1 > 0 && length2 > 0 && *s1Char == *s2Char )
164 while ( length1 > 0 && length2 > 0 && s1.at( commonPrefixLen + length1 - 1 ) == s2.at( commonPrefixLen + length2 - 1 ) )
175 else if ( length2 == 0 )
181 if ( length1 > length2 )
184 std::swap( length1, length2 );
189 col.fill( 0, length2 + 1 );
190 QVector< int > prevCol;
191 prevCol.reserve( length2 + 1 );
192 for (
int i = 0; i < length2 + 1; ++i )
196 const QChar *s2start = s2Char;
197 for (
int i = 0; i < length1; ++i )
201 for (
int j = 0; j < length2; ++j )
203 col[j + 1] = std::min( std::min( 1 + col[j], 1 + prevCol[1 + j] ), prevCol[j] + ( ( *s1Char == *s2Char ) ? 0 : 1 ) );
209 return prevCol[length2];
214 if ( string1.isEmpty() || string2.isEmpty() )
221 QString s1( caseSensitive ? string1 : string1.toLower() );
222 QString s2( caseSensitive ? string2 : string2.toLower() );
230 int *currentScores =
new int [ s2.length()];
231 int *previousScores =
new int [ s2.length()];
232 int maxCommonLength = 0;
233 int lastMaxBeginIndex = 0;
235 const QChar *s1Char = s1.constData();
236 const QChar *s2Char = s2.constData();
237 const QChar *s2Start = s2Char;
239 for (
int i = 0; i < s1.length(); ++i )
241 for (
int j = 0; j < s2.length(); ++j )
243 if ( *s1Char != *s2Char )
245 currentScores[j] = 0;
249 if ( i == 0 || j == 0 )
251 currentScores[j] = 1;
255 currentScores[j] = 1 + previousScores[j - 1];
258 if ( maxCommonLength < currentScores[j] )
260 maxCommonLength = currentScores[j];
261 lastMaxBeginIndex = i;
266 std::swap( currentScores, previousScores );
270 delete [] currentScores;
271 delete [] previousScores;
272 return string1.mid( lastMaxBeginIndex - maxCommonLength + 1, maxCommonLength );
277 if ( string1.isEmpty() && string2.isEmpty() )
283 if ( string1.length() != string2.length() )
290 QString s1( caseSensitive ? string1 : string1.toLower() );
291 QString s2( caseSensitive ? string2 : string2.toLower() );
300 const QChar *s1Char = s1.constData();
301 const QChar *s2Char = s2.constData();
303 for (
int i = 0; i < string1.length(); ++i )
305 if ( *s1Char != *s2Char )
316 if (
string.isEmpty() )
319 QString tmp =
string.toUpper();
322 QChar *char1 = tmp.data();
323 QChar *char2 = tmp.data();
325 for (
int i = 0; i < tmp.length(); ++i, ++char2 )
327 if ( ( *char2 ).unicode() >= 0x41 && ( *char2 ).unicode() <= 0x5A && ( i == 0 || ( ( *char2 ).unicode() != 0x41 && ( *char2 ).unicode() != 0x45
328 && ( *char2 ).unicode() != 0x48 && ( *char2 ).unicode() != 0x49
329 && ( *char2 ).unicode() != 0x4F && ( *char2 ).unicode() != 0x55
330 && ( *char2 ).unicode() != 0x57 && ( *char2 ).unicode() != 0x59 ) ) )
337 tmp.truncate( outLen );
339 QChar *tmpChar = tmp.data();
341 for (
int i = 1; i < tmp.length(); ++i, ++tmpChar )
343 switch ( ( *tmpChar ).unicode() )
349 tmp.replace( i, 1, QChar( 0x31 ) );
360 tmp.replace( i, 1, QChar( 0x32 ) );
365 tmp.replace( i, 1, QChar( 0x33 ) );
369 tmp.replace( i, 1, QChar( 0x34 ) );
374 tmp.replace( i, 1, QChar( 0x35 ) );
378 tmp.replace( i, 1, QChar( 0x36 ) );
388 for (
int i = 1; i < tmp.length(); ++i, ++char2 )
390 if ( *char2 != *char1 )
399 tmp.truncate( outLen );
400 if ( tmp.length() < 4 )
411 QString converted = string;
415 static QRegExp urlRegEx(
"(\\b(([\\w-]+://?|www[.])[^\\s()<>]+(?:\\([\\w\\d]+\\)|([^!\"#$%&'()*+,\\-./:;<=>?@[\\\\\\]^_`{|}~\\s]|/))))" );
416 static QRegExp protoRegEx(
"^(?:f|ht)tps?://" );
417 static QRegExp emailRegEx(
"([\\w._%+-]+@[\\w.-]+\\.[A-Za-z]+)" );
421 while ( urlRegEx.indexIn( converted, offset ) != -1 )
424 QString url = urlRegEx.cap( 1 );
425 QString protoUrl = url;
426 if ( protoRegEx.indexIn( protoUrl ) == -1 )
428 protoUrl.prepend(
"http://" );
430 QString anchor = QStringLiteral(
"<a href=\"%1\">%2</a>" ).arg( protoUrl.toHtmlEscaped(), url.toHtmlEscaped() );
431 converted.replace( urlRegEx.pos( 1 ), url.length(), anchor );
432 offset = urlRegEx.pos( 1 ) + anchor.length();
435 while ( emailRegEx.indexIn( converted, offset ) != -1 )
438 QString email = emailRegEx.cap( 1 );
439 QString anchor = QStringLiteral(
"<a href=\"mailto:%1\">%1</a>" ).arg( email.toHtmlEscaped() );
440 converted.replace( emailRegEx.pos( 1 ), email.length(), anchor );
441 offset = emailRegEx.pos( 1 ) + anchor.length();
450 QString
QgsStringUtils::wordWrap(
const QString &
string,
const int length,
const bool useMaxLineLength,
const QString &customDelimiter )
452 if (
string.isEmpty() || length == 0 )
457 int delimiterLength = 0;
459 if ( !customDelimiter.isEmpty() )
461 rx.setPatternSyntax( QRegExp::FixedString );
462 rx.setPattern( customDelimiter );
463 delimiterLength = customDelimiter.length();
468 rx.setPattern( QStringLiteral(
"[\\s\\x200B]" ) );
472 const QStringList lines =
string.split(
'\n' );
473 int strLength, strCurrent, strHit, lastHit;
475 for (
int i = 0; i < lines.size(); i++ )
477 strLength = lines.at( i ).length();
482 while ( strCurrent < strLength )
486 if ( useMaxLineLength )
489 strHit = lines.at( i ).lastIndexOf( rx, strCurrent + length );
490 if ( strHit == lastHit || strHit == -1 )
493 strHit = lines.at( i ).indexOf( rx, strCurrent + std::abs( length ) );
499 strHit = lines.at( i ).indexOf( rx, strCurrent + std::abs( length ) );
503 newstr.append( lines.at( i ).midRef( strCurrent, strHit - strCurrent ) );
504 newstr.append(
'\n' );
505 strCurrent = strHit + delimiterLength;
509 newstr.append( lines.at( i ).midRef( strCurrent ) );
510 strCurrent = strLength;
513 if ( i < lines.size() - 1 )
514 newstr.append(
'\n' );
522 , mReplacement( replacement )
523 , mCaseSensitive( caseSensitive )
524 , mWholeWordOnly( wholeWordOnly )
526 if ( mWholeWordOnly )
527 mRx = QRegExp( QString(
"\\b%1\\b" ).arg( mMatch ),
528 mCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive );
533 QString result = input;
534 if ( !mWholeWordOnly )
536 return result.replace( mMatch, mReplacement, mCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive );
540 return result.replace( mRx, mReplacement );
547 map.insert( QStringLiteral(
"match" ), mMatch );
548 map.insert( QStringLiteral(
"replace" ), mReplacement );
549 map.insert( QStringLiteral(
"caseSensitive" ), mCaseSensitive ?
"1" :
"0" );
550 map.insert( QStringLiteral(
"wholeWord" ), mWholeWordOnly ?
"1" :
"0" );
557 properties.value( QStringLiteral(
"replace" ) ),
558 properties.value( QStringLiteral(
"caseSensitive" ), QStringLiteral(
"0" ) ) == QLatin1String(
"1" ),
559 properties.value( QStringLiteral(
"wholeWord" ), QStringLiteral(
"0" ) ) == QLatin1String(
"1" ) );
564 QString result = input;
577 QDomElement propEl = doc.createElement( QStringLiteral(
"replacement" ) );
578 QgsStringMap::const_iterator it = props.constBegin();
579 for ( ; it != props.constEnd(); ++it )
581 propEl.setAttribute( it.key(), it.value() );
583 elem.appendChild( propEl );
589 mReplacements.clear();
590 QDomNodeList nodelist = elem.elementsByTagName( QStringLiteral(
"replacement" ) );
591 for (
int i = 0; i < nodelist.count(); i++ )
593 QDomElement replacementElem = nodelist.at( i ).toElement();
594 QDomNamedNodeMap nodeMap = replacementElem.attributes();
597 for (
int j = 0; j < nodeMap.count(); ++j )
599 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.
static QString wordWrap(const QString &string, int length, bool useMaxLineLength=true, const QString &customDelimiter=QString())
Automatically wraps a string by inserting new line characters at appropriate locations in the string...
Simple title case conversion - does not fully grammatically parse the text and uses simple rules only...
Convert the string to upper camel case. Note that this method does not unaccent characters.
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.