diff --git a/src/blackmisc/stringutils.cpp b/src/blackmisc/stringutils.cpp index f8bb44047..0b1486660 100644 --- a/src/blackmisc/stringutils.cpp +++ b/src/blackmisc/stringutils.cpp @@ -56,6 +56,57 @@ namespace BlackMisc return false; } + int fuzzyShortStringComparision(const QString &str1, const QString &str2, Qt::CaseSensitivity cs) + { + // same + if (cs == Qt::CaseInsensitive) { if (caseInsensitiveStringCompare(str1, str2)) { return 100; }} + else if (str1 == str2) { return 100; } + + // one string is empty + if (str1.isEmpty() || str2.isEmpty()) { return 0; } + + // make sure aStr is not shorter + const QString aStr = str1.length() >= str2.length() ? str1 : str2; + const QString bStr = str1.length() >= str2.length() ? str2 : str1; + + // starts/ends with + float s1 = aStr.length(); + float s2 = bStr.length(); + if (aStr.endsWith(bStr, cs)) { return s1 / s2 * 100; } + if (aStr.startsWith(bStr, cs)) { return s1 / s2 * 100; } + + // contains + if (aStr.contains(bStr, cs)) { return s1 / s2 * 100; } + + // char by char + float points = 0; + for (int p = 0; p < aStr.length(); p++) + { + if (p < bStr.length() && aStr[p] == bStr[p]) + { + points += 1.0; + continue; + } + + // char after + const int a = p + 1; + if (a < bStr.length() && aStr[p] == bStr[a]) + { + points += 0.5; + continue; + } + + // char before + const int b = p - 1; + if (b >= 0 && aStr[p] == bStr[b]) + { + points += 0.5; + continue; + } + } + return points / s1 * 100; + } + QString intToHex(int value, int digits) { QString hex(QString::number(value, 16).toUpper()); diff --git a/src/blackmisc/stringutils.h b/src/blackmisc/stringutils.h index f20d77bb6..cb73be40c 100644 --- a/src/blackmisc/stringutils.h +++ b/src/blackmisc/stringutils.h @@ -38,7 +38,7 @@ namespace BlackMisc template QString removeChars(const QString &s, F predicate) { QString result; - std::copy_if(s.begin(), s.end(), std::back_inserter(result), [=](auto c) { return !predicate(c); }); + std::copy_if(s.begin(), s.end(), std::back_inserter(result), [ = ](auto c) { return !predicate(c); }); return result; } @@ -61,7 +61,7 @@ namespace BlackMisc template QList splitStringRefs(const QString &s, F predicate) { QList result; - auto notPredicate = [=](auto c) { return !predicate(c); }; + auto notPredicate = [ = ](auto c) { return !predicate(c); }; auto begin = s.begin(); while (true) { @@ -132,6 +132,10 @@ namespace BlackMisc //! Convert string to bool BLACKMISC_EXPORT bool stringToBool(const QString &boolString); + //! Fuzzy compare for short strings (like ICAO designators) + //! \return int 0..100 (100 is perfect match) + BLACKMISC_EXPORT int fuzzyShortStringComparision(const QString &str1, const QString &str2, Qt::CaseSensitivity cs = Qt::CaseSensitive); + //! Int to hex value BLACKMISC_EXPORT QString intToHex(int value, int digits = 2); @@ -230,10 +234,10 @@ namespace BlackMisc * the derived class uses this macro to disambiguate the inherited members. */ # define BLACKMISC_DECLARE_USING_MIXIN_STRING(DERIVED) \ - using ::BlackMisc::Mixin::String::toQString; \ - using ::BlackMisc::Mixin::String::toFormattedQString; \ - using ::BlackMisc::Mixin::String::toStdString; \ - using ::BlackMisc::Mixin::String::stringForStreaming; + using ::BlackMisc::Mixin::String::toQString; \ + using ::BlackMisc::Mixin::String::toFormattedQString; \ + using ::BlackMisc::Mixin::String::toStdString; \ + using ::BlackMisc::Mixin::String::stringForStreaming; } // ns } // ns