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 );
103 result.remove(
' ' );
114 for (
int i = 0; i <
string.size(); ++i )
116 QChar ch =
string.at( i );
117 if ( ch.unicode() > 160 )
118 encoded += QStringLiteral(
"&#%1;" ).arg( static_cast< int >( ch.unicode() ) );
119 else if ( ch.unicode() == 38 )
120 encoded += QStringLiteral(
"&" );
121 else if ( ch.unicode() == 60 )
122 encoded += QStringLiteral(
"<" );
123 else if ( ch.unicode() == 62 )
124 encoded += QStringLiteral(
">" );
133 int length1 = string1.length();
134 int length2 = string2.length();
137 if ( string1.isEmpty() )
141 else if ( string2.isEmpty() )
147 QString s1( caseSensitive ? string1 : string1.toLower() );
148 QString s2( caseSensitive ? string2 : string2.toLower() );
150 const QChar *s1Char = s1.constData();
151 const QChar *s2Char = s2.constData();
154 int commonPrefixLen = 0;
155 while ( length1 > 0 && length2 > 0 && *s1Char == *s2Char )
165 while ( length1 > 0 && length2 > 0 && s1.at( commonPrefixLen + length1 - 1 ) == s2.at( commonPrefixLen + length2 - 1 ) )
176 else if ( length2 == 0 )
182 if ( length1 > length2 )
185 std::swap( length1, length2 );
190 col.fill( 0, length2 + 1 );
191 QVector< int > prevCol;
192 prevCol.reserve( length2 + 1 );
193 for (
int i = 0; i < length2 + 1; ++i )
197 const QChar *s2start = s2Char;
198 for (
int i = 0; i < length1; ++i )
202 for (
int j = 0; j < length2; ++j )
204 col[j + 1] = std::min( std::min( 1 + col[j], 1 + prevCol[1 + j] ), prevCol[j] + ( ( *s1Char == *s2Char ) ? 0 : 1 ) );
210 return prevCol[length2];
215 if ( string1.isEmpty() || string2.isEmpty() )
222 QString s1( caseSensitive ? string1 : string1.toLower() );
223 QString s2( caseSensitive ? string2 : string2.toLower() );
231 int *currentScores =
new int [ s2.length()];
232 int *previousScores =
new int [ s2.length()];
233 int maxCommonLength = 0;
234 int lastMaxBeginIndex = 0;
236 const QChar *s1Char = s1.constData();
237 const QChar *s2Char = s2.constData();
238 const QChar *s2Start = s2Char;
240 for (
int i = 0; i < s1.length(); ++i )
242 for (
int j = 0; j < s2.length(); ++j )
244 if ( *s1Char != *s2Char )
246 currentScores[j] = 0;
250 if ( i == 0 || j == 0 )
252 currentScores[j] = 1;
256 currentScores[j] = 1 + previousScores[j - 1];
259 if ( maxCommonLength < currentScores[j] )
261 maxCommonLength = currentScores[j];
262 lastMaxBeginIndex = i;
267 std::swap( currentScores, previousScores );
271 delete [] currentScores;
272 delete [] previousScores;
273 return string1.mid( lastMaxBeginIndex - maxCommonLength + 1, maxCommonLength );
278 if ( string1.isEmpty() && string2.isEmpty() )
284 if ( string1.length() != string2.length() )
291 QString s1( caseSensitive ? string1 : string1.toLower() );
292 QString s2( caseSensitive ? string2 : string2.toLower() );
301 const QChar *s1Char = s1.constData();
302 const QChar *s2Char = s2.constData();
304 for (
int i = 0; i < string1.length(); ++i )
306 if ( *s1Char != *s2Char )
317 if (
string.isEmpty() )
320 QString tmp =
string.toUpper();
323 QChar *char1 = tmp.data();
324 QChar *char2 = tmp.data();
326 for (
int i = 0; i < tmp.length(); ++i, ++char2 )
328 if ( ( *char2 ).unicode() >= 0x41 && ( *char2 ).unicode() <= 0x5A && ( i == 0 || ( ( *char2 ).unicode() != 0x41 && ( *char2 ).unicode() != 0x45
329 && ( *char2 ).unicode() != 0x48 && ( *char2 ).unicode() != 0x49
330 && ( *char2 ).unicode() != 0x4F && ( *char2 ).unicode() != 0x55
331 && ( *char2 ).unicode() != 0x57 && ( *char2 ).unicode() != 0x59 ) ) )
338 tmp.truncate( outLen );
340 QChar *tmpChar = tmp.data();
342 for (
int i = 1; i < tmp.length(); ++i, ++tmpChar )
344 switch ( ( *tmpChar ).unicode() )
350 tmp.replace( i, 1, QChar( 0x31 ) );
361 tmp.replace( i, 1, QChar( 0x32 ) );
366 tmp.replace( i, 1, QChar( 0x33 ) );
370 tmp.replace( i, 1, QChar( 0x34 ) );
375 tmp.replace( i, 1, QChar( 0x35 ) );
379 tmp.replace( i, 1, QChar( 0x36 ) );
389 for (
int i = 1; i < tmp.length(); ++i, ++char2 )
391 if ( *char2 != *char1 )
400 tmp.truncate( outLen );
401 if ( tmp.length() < 4 )
412 QString converted = string;
416 static QRegExp urlRegEx(
"(\\b(([\\w-]+://?|www[.])[^\\s()<>]+(?:\\([\\w\\d]+\\)|([^!\"#$%&'()*+,\\-./:;<=>?@[\\\\\\]^_`{|}~\\s]|/))))" );
417 static QRegExp protoRegEx(
"^(?:f|ht)tps?://|file://" );
418 static QRegExp emailRegEx(
"([\\w._%+-]+@[\\w.-]+\\.[A-Za-z]+)" );
422 while ( urlRegEx.indexIn( converted, offset ) != -1 )
425 QString url = urlRegEx.cap( 1 );
426 QString protoUrl = url;
427 if ( protoRegEx.indexIn( protoUrl ) == -1 )
429 protoUrl.prepend(
"http://" );
431 QString anchor = QStringLiteral(
"<a href=\"%1\">%2</a>" ).arg( protoUrl.toHtmlEscaped(), url.toHtmlEscaped() );
432 converted.replace( urlRegEx.pos( 1 ), url.length(), anchor );
433 offset = urlRegEx.pos( 1 ) + anchor.length();
436 while ( emailRegEx.indexIn( converted, offset ) != -1 )
439 QString email = emailRegEx.cap( 1 );
440 QString anchor = QStringLiteral(
"<a href=\"mailto:%1\">%1</a>" ).arg( email.toHtmlEscaped() );
441 converted.replace( emailRegEx.pos( 1 ), email.length(), anchor );
442 offset = emailRegEx.pos( 1 ) + anchor.length();
451 QString
QgsStringUtils::wordWrap(
const QString &
string,
const int length,
const bool useMaxLineLength,
const QString &customDelimiter )
453 if (
string.isEmpty() || length == 0 )
458 int delimiterLength = 0;
460 if ( !customDelimiter.isEmpty() )
462 rx.setPatternSyntax( QRegExp::FixedString );
463 rx.setPattern( customDelimiter );
464 delimiterLength = customDelimiter.length();
469 rx.setPattern( QStringLiteral(
"[\\s\\x200B]" ) );
473 const QStringList lines =
string.split(
'\n' );
474 int strLength, strCurrent, strHit, lastHit;
476 for (
int i = 0; i < lines.size(); i++ )
478 strLength = lines.at( i ).length();
483 while ( strCurrent < strLength )
487 if ( useMaxLineLength )
490 strHit = lines.at( i ).lastIndexOf( rx, strCurrent + length );
491 if ( strHit == lastHit || strHit == -1 )
494 strHit = lines.at( i ).indexOf( rx, strCurrent + std::abs( length ) );
500 strHit = lines.at( i ).indexOf( rx, strCurrent + std::abs( length ) );
504 newstr.append( lines.at( i ).midRef( strCurrent, strHit - strCurrent ) );
505 newstr.append(
'\n' );
506 strCurrent = strHit + delimiterLength;
510 newstr.append( lines.at( i ).midRef( strCurrent ) );
511 strCurrent = strLength;
514 if ( i < lines.size() - 1 )
515 newstr.append(
'\n' );
523 , mReplacement( replacement )
524 , mCaseSensitive( caseSensitive )
525 , mWholeWordOnly( wholeWordOnly )
527 if ( mWholeWordOnly )
528 mRx = QRegExp( QString(
"\\b%1\\b" ).arg( mMatch ),
529 mCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive );
534 QString result = input;
535 if ( !mWholeWordOnly )
537 return result.replace( mMatch, mReplacement, mCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive );
541 return result.replace( mRx, mReplacement );
548 map.insert( QStringLiteral(
"match" ), mMatch );
549 map.insert( QStringLiteral(
"replace" ), mReplacement );
550 map.insert( QStringLiteral(
"caseSensitive" ), mCaseSensitive ?
"1" :
"0" );
551 map.insert( QStringLiteral(
"wholeWord" ), mWholeWordOnly ?
"1" :
"0" );
558 properties.value( QStringLiteral(
"replace" ) ),
559 properties.value( QStringLiteral(
"caseSensitive" ), QStringLiteral(
"0" ) ) == QLatin1String(
"1" ),
560 properties.value( QStringLiteral(
"wholeWord" ), QStringLiteral(
"0" ) ) == QLatin1String(
"1" ) );
565 QString result = input;
566 const auto constMReplacements = mReplacements;
569 result = r.process( result );
576 const auto constMReplacements = mReplacements;
580 QDomElement propEl = doc.createElement( QStringLiteral(
"replacement" ) );
581 QgsStringMap::const_iterator it = props.constBegin();
582 for ( ; it != props.constEnd(); ++it )
584 propEl.setAttribute( it.key(), it.value() );
586 elem.appendChild( propEl );
592 mReplacements.clear();
593 QDomNodeList nodelist = elem.elementsByTagName( QStringLiteral(
"replacement" ) );
594 for (
int i = 0; i < nodelist.count(); i++ )
596 QDomElement replacementElem = nodelist.at( i ).toElement();
597 QDomNamedNodeMap nodeMap = replacementElem.attributes();
600 for (
int j = 0; j < nodeMap.count(); ++j )
602 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.