19 #include <QStringList> 20 #include <QTextBoundaryFinder> 21 #include <QRegularExpression> 26 if (
string.isEmpty() )
29 switch ( capitalization )
35 return string.toUpper();
38 return string.toLower();
42 QString temp = string;
44 QTextBoundaryFinder wordSplitter( QTextBoundaryFinder::Word,
string.constData(),
string.length(),
nullptr, 0 );
45 QTextBoundaryFinder letterSplitter( QTextBoundaryFinder::Grapheme,
string.constData(),
string.length(),
nullptr, 0 );
47 wordSplitter.setPosition( 0 );
49 while ( ( first && wordSplitter.boundaryReasons() & QTextBoundaryFinder::StartOfItem )
50 || wordSplitter.toNextBoundary() >= 0 )
53 letterSplitter.setPosition( wordSplitter.position() );
54 letterSplitter.toNextBoundary();
55 QString substr =
string.mid( wordSplitter.position(), letterSplitter.position() - wordSplitter.position() );
56 temp.replace( wordSplitter.position(), substr.length(), substr.toUpper() );
65 static QStringList smallWords;
66 static QStringList newPhraseSeparators;
67 static QRegularExpression splitWords;
68 if ( smallWords.empty() )
70 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(
'|' );
71 newPhraseSeparators = QObject::tr(
".|:" ).split(
'|' );
72 splitWords = QRegularExpression( QStringLiteral(
"\\b" ), QRegularExpression::UseUnicodePropertiesOption );
75 const QStringList parts =
string.split( splitWords, QString::SkipEmptyParts );
77 bool firstWord =
true;
79 int lastWord = parts.count() - 1;
80 for (
const QString &word : qgis::as_const( parts ) )
82 if ( newPhraseSeparators.contains( word.trimmed() ) )
87 else if ( firstWord || ( i == lastWord ) || !smallWords.contains( word ) )
89 result += word.at( 0 ).toUpper() + word.mid( 1 );
109 for (
int i = 0; i <
string.size(); ++i )
111 QChar ch =
string.at( i );
112 if ( ch.unicode() > 160 )
113 encoded += QStringLiteral(
"&#%1;" ).arg( static_cast< int >( ch.unicode() ) );
114 else if ( ch.unicode() == 38 )
115 encoded += QStringLiteral(
"&" );
116 else if ( ch.unicode() == 60 )
117 encoded += QStringLiteral(
"<" );
118 else if ( ch.unicode() == 62 )
119 encoded += QStringLiteral(
">" );
128 int length1 = string1.length();
129 int length2 = string2.length();
132 if ( string1.isEmpty() )
136 else if ( string2.isEmpty() )
142 QString s1( caseSensitive ? string1 : string1.toLower() );
143 QString s2( caseSensitive ? string2 : string2.toLower() );
145 const QChar *s1Char = s1.constData();
146 const QChar *s2Char = s2.constData();
149 int commonPrefixLen = 0;
150 while ( length1 > 0 && length2 > 0 && *s1Char == *s2Char )
160 while ( length1 > 0 && length2 > 0 && s1.at( commonPrefixLen + length1 - 1 ) == s2.at( commonPrefixLen + length2 - 1 ) )
171 else if ( length2 == 0 )
177 if ( length1 > length2 )
180 std::swap( length1, length2 );
185 col.fill( 0, length2 + 1 );
186 QVector< int > prevCol;
187 prevCol.reserve( length2 + 1 );
188 for (
int i = 0; i < length2 + 1; ++i )
192 const QChar *s2start = s2Char;
193 for (
int i = 0; i < length1; ++i )
197 for (
int j = 0; j < length2; ++j )
199 col[j + 1] = std::min( std::min( 1 + col[j], 1 + prevCol[1 + j] ), prevCol[j] + ( ( *s1Char == *s2Char ) ? 0 : 1 ) );
205 return prevCol[length2];
210 if ( string1.isEmpty() || string2.isEmpty() )
217 QString s1( caseSensitive ? string1 : string1.toLower() );
218 QString s2( caseSensitive ? string2 : string2.toLower() );
226 int *currentScores =
new int [ s2.length()];
227 int *previousScores =
new int [ s2.length()];
228 int maxCommonLength = 0;
229 int lastMaxBeginIndex = 0;
231 const QChar *s1Char = s1.constData();
232 const QChar *s2Char = s2.constData();
233 const QChar *s2Start = s2Char;
235 for (
int i = 0; i < s1.length(); ++i )
237 for (
int j = 0; j < s2.length(); ++j )
239 if ( *s1Char != *s2Char )
241 currentScores[j] = 0;
245 if ( i == 0 || j == 0 )
247 currentScores[j] = 1;
251 currentScores[j] = 1 + previousScores[j - 1];
254 if ( maxCommonLength < currentScores[j] )
256 maxCommonLength = currentScores[j];
257 lastMaxBeginIndex = i;
262 std::swap( currentScores, previousScores );
266 delete [] currentScores;
267 delete [] previousScores;
268 return string1.mid( lastMaxBeginIndex - maxCommonLength + 1, maxCommonLength );
273 if ( string1.isEmpty() && string2.isEmpty() )
279 if ( string1.length() != string2.length() )
286 QString s1( caseSensitive ? string1 : string1.toLower() );
287 QString s2( caseSensitive ? string2 : string2.toLower() );
296 const QChar *s1Char = s1.constData();
297 const QChar *s2Char = s2.constData();
299 for (
int i = 0; i < string1.length(); ++i )
301 if ( *s1Char != *s2Char )
312 if (
string.isEmpty() )
315 QString tmp =
string.toUpper();
318 QChar *char1 = tmp.data();
319 QChar *char2 = tmp.data();
321 for (
int i = 0; i < tmp.length(); ++i, ++char2 )
323 if ( ( *char2 ).unicode() >= 0x41 && ( *char2 ).unicode() <= 0x5A && ( i == 0 || ( ( *char2 ).unicode() != 0x41 && ( *char2 ).unicode() != 0x45
324 && ( *char2 ).unicode() != 0x48 && ( *char2 ).unicode() != 0x49
325 && ( *char2 ).unicode() != 0x4F && ( *char2 ).unicode() != 0x55
326 && ( *char2 ).unicode() != 0x57 && ( *char2 ).unicode() != 0x59 ) ) )
333 tmp.truncate( outLen );
335 QChar *tmpChar = tmp.data();
337 for (
int i = 1; i < tmp.length(); ++i, ++tmpChar )
339 switch ( ( *tmpChar ).unicode() )
345 tmp.replace( i, 1, QChar( 0x31 ) );
356 tmp.replace( i, 1, QChar( 0x32 ) );
361 tmp.replace( i, 1, QChar( 0x33 ) );
365 tmp.replace( i, 1, QChar( 0x34 ) );
370 tmp.replace( i, 1, QChar( 0x35 ) );
374 tmp.replace( i, 1, QChar( 0x36 ) );
384 for (
int i = 1; i < tmp.length(); ++i, ++char2 )
386 if ( *char2 != *char1 )
395 tmp.truncate( outLen );
396 if ( tmp.length() < 4 )
407 QString converted = string;
411 static QRegExp urlRegEx(
"(\\b(([\\w-]+://?|www[.])[^\\s()<>]+(?:\\([\\w\\d]+\\)|([^!\"#$%&'()*+,\\-./:;<=>?@[\\\\\\]^_`{|}~\\s]|/))))" );
412 static QRegExp protoRegEx(
"^(?:f|ht)tps?://|file://" );
413 static QRegExp emailRegEx(
"([\\w._%+-]+@[\\w.-]+\\.[A-Za-z]+)" );
417 while ( urlRegEx.indexIn( converted, offset ) != -1 )
420 QString url = urlRegEx.cap( 1 );
421 QString protoUrl = url;
422 if ( protoRegEx.indexIn( protoUrl ) == -1 )
424 protoUrl.prepend(
"http://" );
426 QString anchor = QStringLiteral(
"<a href=\"%1\">%2</a>" ).arg( protoUrl.toHtmlEscaped(), url.toHtmlEscaped() );
427 converted.replace( urlRegEx.pos( 1 ), url.length(), anchor );
428 offset = urlRegEx.pos( 1 ) + anchor.length();
431 while ( emailRegEx.indexIn( converted, offset ) != -1 )
434 QString email = emailRegEx.cap( 1 );
435 QString anchor = QStringLiteral(
"<a href=\"mailto:%1\">%1</a>" ).arg( email.toHtmlEscaped() );
436 converted.replace( emailRegEx.pos( 1 ), email.length(), anchor );
437 offset = emailRegEx.pos( 1 ) + anchor.length();
446 QString
QgsStringUtils::wordWrap(
const QString &
string,
const int length,
const bool useMaxLineLength,
const QString &customDelimiter )
448 if (
string.isEmpty() || length == 0 )
453 int delimiterLength = 0;
455 if ( !customDelimiter.isEmpty() )
457 rx.setPatternSyntax( QRegExp::FixedString );
458 rx.setPattern( customDelimiter );
459 delimiterLength = customDelimiter.length();
464 rx.setPattern( QStringLiteral(
"[\\s\\x200B]" ) );
468 const QStringList lines =
string.split(
'\n' );
469 int strLength, strCurrent, strHit, lastHit;
471 for (
int i = 0; i < lines.size(); i++ )
473 strLength = lines.at( i ).length();
478 while ( strCurrent < strLength )
482 if ( useMaxLineLength )
485 strHit = lines.at( i ).lastIndexOf( rx, strCurrent + length );
486 if ( strHit == lastHit || strHit == -1 )
489 strHit = lines.at( i ).indexOf( rx, strCurrent + std::abs( length ) );
495 strHit = lines.at( i ).indexOf( rx, strCurrent + std::abs( length ) );
499 newstr.append( lines.at( i ).midRef( strCurrent, strHit - strCurrent ) );
500 newstr.append(
'\n' );
501 strCurrent = strHit + delimiterLength;
505 newstr.append( lines.at( i ).midRef( strCurrent ) );
506 strCurrent = strLength;
509 if ( i < lines.size() - 1 )
510 newstr.append(
'\n' );
518 , mReplacement( replacement )
519 , mCaseSensitive( caseSensitive )
520 , mWholeWordOnly( wholeWordOnly )
522 if ( mWholeWordOnly )
523 mRx = QRegExp( QString(
"\\b%1\\b" ).arg( mMatch ),
524 mCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive );
529 QString result = input;
530 if ( !mWholeWordOnly )
532 return result.replace( mMatch, mReplacement, mCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive );
536 return result.replace( mRx, mReplacement );
543 map.insert( QStringLiteral(
"match" ), mMatch );
544 map.insert( QStringLiteral(
"replace" ), mReplacement );
545 map.insert( QStringLiteral(
"caseSensitive" ), mCaseSensitive ?
"1" :
"0" );
546 map.insert( QStringLiteral(
"wholeWord" ), mWholeWordOnly ?
"1" :
"0" );
553 properties.value( QStringLiteral(
"replace" ) ),
554 properties.value( QStringLiteral(
"caseSensitive" ), QStringLiteral(
"0" ) ) == QLatin1String(
"1" ),
555 properties.value( QStringLiteral(
"wholeWord" ), QStringLiteral(
"0" ) ) == QLatin1String(
"1" ) );
560 QString result = input;
573 QDomElement propEl = doc.createElement( QStringLiteral(
"replacement" ) );
574 QgsStringMap::const_iterator it = props.constBegin();
575 for ( ; it != props.constEnd(); ++it )
577 propEl.setAttribute( it.key(), it.value() );
579 elem.appendChild( propEl );
585 mReplacements.clear();
586 QDomNodeList nodelist = elem.elementsByTagName( QStringLiteral(
"replacement" ) );
587 for (
int i = 0; i < nodelist.count(); i++ )
589 QDomElement replacementElem = nodelist.at( i ).toElement();
590 QDomNamedNodeMap nodeMap = replacementElem.attributes();
593 for (
int j = 0; j < nodeMap.count(); ++j )
595 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...
QMap< QString, QString > QgsStringMap
static QString soundex(const QString &string)
Returns the Soundex representation of a string.
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.
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 using QgsStringR...
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.
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 ...
QgsStringMap properties() const
Returns a map of the replacement properties.
void writeXml(QDomElement &elem, QDomDocument &doc) const
Writes the collection state to an XML element.
static int hammingDistance(const QString &string1, const QString &string2, bool caseSensitive=false)
Returns the Hamming distance between two strings.