Files
pilotclient/src/blackmisc/stringutils.h

330 lines
12 KiB
C++

/* Copyright (C) 2015
* swift project Community / Contributors
*
* This file is part of swift project. It is subject to the license terms in the LICENSE file found in the top-level
* directory of this distribution and at http://www.swift-project.org/license.html. No part of swift project,
* including this file, may be copied, modified, propagated, or distributed except according to the terms
* contained in the LICENSE file.
*/
//! \file
#ifndef BLACKMISC_STRINGUTILS_H
#define BLACKMISC_STRINGUTILS_H
#include "blackmisc/blackmiscexport.h"
#include "blackmisc/range.h"
#include <QByteArray>
#include <QDataStream>
#include <QDateTime>
#include <QDebug>
#include <QList>
#include <QMapIterator>
#include <QString>
#include <QStringRef>
#include <QTextStream>
#include <QtGlobal>
#include <iosfwd>
#include <string>
#include <algorithm>
template <class T1, class T2> class QMap;
//! Free functions in BlackMisc
namespace BlackMisc
{
//! Return a string with characters removed that match the given predicate.
template <class F> 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); });
return result;
}
//! Remove the typical separators such as "-", " "
BLACKMISC_EXPORT QString removeDateTimeSeparators(const QString &s);
//! True if any character in the string matches the given predicate.
template <class F> bool containsChar(const QString &s, F predicate)
{
return std::any_of(s.begin(), s.end(), predicate);
}
//! Index of first character in the string matching the given predicate, or -1 if not found.
template <class F> int indexOfChar(const QString &s, F predicate)
{
auto it = std::find_if(s.begin(), s.end(), predicate);
if (it == s.end()) { return -1; }
return static_cast<int>(std::distance(s.begin(), it));
}
//! nth index of ch
BLACKMISC_EXPORT int nthIndexOf(const QString &string, QChar ch, int nth = 1, Qt::CaseSensitivity cs = Qt::CaseInsensitive);
//! Split a string into multiple strings, using a predicate function to identify the split points.
//! \warning The returned refs are only valid during the lifetime of the original string.
template <class F> QList<QStringRef> splitStringRefs(const QString &s, F predicate)
{
QList<QStringRef> result;
auto notPredicate = [ = ](auto c) { return !predicate(c); };
auto begin = s.begin();
while (true)
{
begin = std::find_if(begin, s.end(), notPredicate);
if (begin == s.end()) { return result; }
auto end = std::find_if(begin, s.end(), predicate);
result.push_back(QStringRef(&s, std::distance(s.begin(), begin), std::distance(begin, end)));
begin = end;
}
}
//! Split a string into multiple lines. Blank lines are skipped.
//! \warning The returned refs are only valid during the lifetime of the original string.
BLACKMISC_EXPORT QList<QStringRef> splitLinesRefs(const QString &s);
//! It would be risky to call splitStringRefs with an rvalue, so forbid it.
template <class F> void splitStringRefs(const QString &&, F) = delete;
//! It would be risky to call splitLinesRefs with an rvalue, so forbid it.
void splitLinesRefs(const QString &&) = delete;
//! Split a string into multiple strings, using a predicate function to identify the split points.
template <class F> QStringList splitString(const QString &s, F predicate)
{
return makeRange(splitStringRefs(s, predicate)).transform([](QStringRef sr) { return sr.toString(); });
}
//! Split a string into multiple lines. Blank lines are skipped.
BLACKMISC_EXPORT QStringList splitLines(const QString &s);
//! A map converted to string
template<class K, class V> QString qmapToString(const QMap<K, V> &map)
{
QString s;
static const QString kv("%1: %2 ");
QMapIterator<K, V> i(map);
while (i.hasNext())
{
i.next();
s.append(
kv.arg(i.key()).arg(i.value())
);
}
return s.trimmed();
}
//! Is 0-9 char, isDigit allows a bunch of more characters
inline bool is09(const QChar &c) { return c >= u'0' && c <= u'9'; }
//! Is 0-9, or ","/";"
inline bool is09OrSeparator(const QChar &c) { return is09(c) || c == u',' || c == '.'; }
//! Safe "at" function, returns empty string if index does not exists
inline const QString &safeAt(const QStringList &stringList, int index)
{
if (stringList.size() > index) { return stringList.at(index); }
static const QString empty;
return empty;
}
//! String with digits only
inline bool isDigitsOnlyString(const QString &testString)
{
return !containsChar(testString, [](QChar c) { return !c.isDigit(); });
}
//! String with 0-9 only
inline bool is09OnlyString(const QString &testString)
{
return !containsChar(testString, [](QChar c) { return !is09(c); });
}
//! String with 0-9/separator only
inline bool is09OrSeparatorOnlyString(const QString &testString)
{
return !containsChar(testString, [](QChar c) { return !is09OrSeparator(c); });
}
//! String only with digits
inline QString digitOnlyString(const QString &string)
{
return removeChars(string, [](QChar c) { return !c.isDigit(); });
}
//! String only with 0-9
inline QString char09OnlyString(const QString &string)
{
return removeChars(string, [](QChar c) { return !is09(c); });
}
//! String only with 0-9
inline QString char09OrSeparatorOnlyString(const QString &string)
{
return removeChars(string, [](QChar c) { return !is09OrSeparator(c); });
}
//! Return string in apostrophes
BLACKMISC_EXPORT const QString inApostrophes(const QString &in, bool ignoreEmpty = false);
//! Return string in quotes
BLACKMISC_EXPORT const QString inQuotes(const QString &in, bool ignoreEmpty = false);
//! Bool to on/off
BLACKMISC_EXPORT const QString &boolToOnOff(bool v);
//! Bool to yes/no
BLACKMISC_EXPORT const QString &boolToYesNo(bool v);
//! Bool to true/false
BLACKMISC_EXPORT const QString &boolToTrueFalse(bool v);
//! Bool to enabled/disabled
BLACKMISC_EXPORT const QString &boolToEnabledDisabled(bool v);
//! Bool isNull to null/no null
BLACKMISC_EXPORT const QString &boolToNullNotNull(bool isNull);
//! 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);
//! Replace dot '.' by locale decimal point
BLACKMISC_EXPORT QString dotToLocaleDecimalPoint(QString &input);
//! Replace dot '.' by locale decimal point
BLACKMISC_EXPORT QString dotToLocaleDecimalPoint(const QString &input);
//! Int to hex value (per byte, 2 digits)
BLACKMISC_EXPORT QString bytesToHexString(const QByteArray &bytes);
//! Byte array from hex value string per byte, 2 digits
BLACKMISC_EXPORT QByteArray byteArrayFromHexString(const QString &hexString);
//! Strip a designator from a combined string
BLACKMISC_EXPORT QString stripDesignatorFromCompleterString(const QString &candidate);
//! Strip a designator from a combined string
BLACKMISC_EXPORT QStringList textCodecNames(bool simpleNames, bool mibNames);
//! Remove accents / diacritic marks from a string
BLACKMISC_EXPORT QString removeAccents(const QString &candidate);
//! Case insensitive string compare
BLACKMISC_EXPORT bool caseInsensitiveStringCompare(const QString &c1, const QString &c2);
//! String compare
BLACKMISC_EXPORT bool stringCompare(const QString &c1, const QString &c2, Qt::CaseSensitivity cs);
//! Get a simplified upper case name for searching by removing all characters except A-Z
BLACKMISC_EXPORT QString simplifyNameForSearch(const QString &name);
//! Add a question mark at the end if not existing
BLACKMISC_EXPORT QString withQuestionMark(const QString &question);
//! Same as QDateTime::fromString but QDateTime will be set to UTC
//! \remark potentially slow, so only to be used when format is unknown
BLACKMISC_EXPORT QDateTime fromStringUtc(const QString &dateTimeString, const QString &format);
//! Same as QDateTime::fromString but QDateTime will be set to UTC
//! \remark potentially slow, so only to be used when format is unknown
BLACKMISC_EXPORT QDateTime fromStringUtc(const QString &dateTimeString, Qt::DateFormat format = Qt::TextDate);
//! Parse multiple date time formats
//! \remark potentially slow, so only to be used when format is unknown
//! \remark TZ is UTC
BLACKMISC_EXPORT QDateTime parseMultipleDateTimeFormats(const QString &dateTimeString);
//! Parse yyyyMMddHHmmsszzz strings optimized
//! \remark string needs to be cleaned up and containing only numbers
//! \remark TZ is UTC
BLACKMISC_EXPORT QDateTime parseDateTimeStringOptimized(const QString &dateTimeString);
namespace Mixin
{
/*!
* CRTP class template from which a derived class can inherit string streaming operations.
*
* \tparam Derived Must implement a public method QString convertToQString(bool i18n = false) const.
*
* \see BLACKMISC_DECLARE_USING_MIXIN_STRING
*/
template <class Derived>
class String
{
public:
//! Stream << overload to be used in debugging messages
friend QDebug operator<<(QDebug debug, const Derived &obj)
{
debug << obj.stringForStreaming();
return debug;
}
//! Operator << when there is no debug stream
friend QNoDebug operator<<(QNoDebug nodebug, const Derived &obj)
{
Q_UNUSED(obj);
return nodebug;
}
//! Operator << based on text stream
friend QTextStream &operator<<(QTextStream &stream, const Derived &obj)
{
stream << obj.stringForStreaming();
return stream;
}
//! Operator << for QDataStream
friend QDataStream &operator<<(QDataStream &stream, const Derived &obj)
{
stream << obj.stringForStreaming();
return stream;
}
//! Stream operator << for std::cout
friend std::ostream &operator<<(std::ostream &ostr, const Derived &obj)
{
ostr << obj.stringForStreaming().toStdString();
return ostr;
}
//! Cast as QString
QString toQString(bool i18n = false) const { return derived()->convertToQString(i18n); }
//! Cast to pretty-printed QString
//! \deprecated not really used and just using toQString
QString toFormattedQString(bool i18n = false) const { return derived()->toQString(i18n); }
//! To std string
std::string toStdString(bool i18n = false) const { return derived()->convertToQString(i18n).toStdString(); }
//! String for streaming operators
QString stringForStreaming() const { return derived()->convertToQString(); }
private:
const Derived *derived() const { return static_cast<const Derived *>(this); }
Derived *derived() { return static_cast<Derived *>(this); }
};
// *INDENT-OFF*
/*!
* When a derived class and a base class both inherit from Mixin::String,
* the derived class uses this macro to disambiguate the inherited members.
*/
# define BLACKMISC_DECLARE_USING_MIXIN_STRING(DERIVED) \
using ::BlackMisc::Mixin::String<DERIVED>::toQString; \
using ::BlackMisc::Mixin::String<DERIVED>::toFormattedQString; \
using ::BlackMisc::Mixin::String<DERIVED>::toStdString; \
using ::BlackMisc::Mixin::String<DERIVED>::stringForStreaming;
// *INDENT-ON*
} // ns
} // ns
#endif // guard