refs #710 Simple algorithms to replace several regular expressions.

This commit is contained in:
Mathew Sutcliffe
2016-08-30 04:02:00 +01:00
parent d2aa2e9c1c
commit 87eeac4e15
13 changed files with 218 additions and 57 deletions

View File

@@ -450,11 +450,8 @@ namespace BlackMisc
bool CAircraftIcaoCode::isValidDesignator(const QString &designator)
{
if (designator.length() < 2 || designator.length() > 5) { return false; }
static QThreadStorage<QRegularExpression> tsRegex;
if (! tsRegex.hasLocalData()) { tsRegex.setLocalData(QRegularExpression("^[A-Z]+[A-Z0-9]*$")); }
const QRegularExpression &regexp = tsRegex.localData();
return (regexp.match(designator).hasMatch());
if (!designator[0].isUpper()) { return false; }
return !containsChar(designator, [](QChar c) { return !c.isUpper() && !c.isDigit(); });
}
bool CAircraftIcaoCode::isValidCombinedType(const QString &combinedType)
@@ -462,11 +459,14 @@ namespace BlackMisc
if (combinedType.length() != 3) { return false; }
// Amphibian, Glider, Helicopter, Seaplane, Landplane, Tilt wing
static const QString validDescriptions = "AGHSLT";
// Electric, Jet, Piston, Turpoprop
static QThreadStorage<QRegularExpression> tsRegex;
if (! tsRegex.hasLocalData()) { tsRegex.setLocalData(QRegularExpression("^[AGHSLT][0-9][EJPT]$")); }
const QRegularExpression &regexp = tsRegex.localData();
return (regexp.match(combinedType).hasMatch());
static const QString validEngines = "EJPT";
if (!validDescriptions.contains(combinedType[0])) { return false; }
if (!combinedType[1].isDigit()) { return false; }
if (!validEngines.contains(combinedType[2])) { return false; }
return true;
}
bool CAircraftIcaoCode::isValidWtc(const QString &candidate)
@@ -491,16 +491,11 @@ namespace BlackMisc
return s;
}
QString CAircraftIcaoCode::normalizeDesignator(const QString candidate)
QString CAircraftIcaoCode::normalizeDesignator(const QString &candidate)
{
QString n(candidate.trimmed().toUpper());
if (n.contains(' ')) { n = n.left(n.indexOf(' ')); } // cutoff as first space
if (n.isEmpty()) { return n; }
static QThreadStorage<QRegularExpression> tsRegex;
if (! tsRegex.hasLocalData()) { tsRegex.setLocalData(QRegularExpression("[^a-zA-Z\\d\\s]")); }
const QRegularExpression &regexp = tsRegex.localData();
return n.remove(regexp);
n = n.left(indexOfChar(n, [](QChar c) { return c.isSpace(); }));
return removeChars(n, [](QChar c) { return !c.isLetterOrNumber(); });
}
QStringList CAircraftIcaoCode::alternativeCombinedCodes(const QString &combinedCode)

View File

@@ -259,7 +259,7 @@ namespace BlackMisc
static const QStringList &getSpecialDesignators();
//! Normalize designator, remove illegal characters
static QString normalizeDesignator(const QString candidate);
static QString normalizeDesignator(const QString &candidate);
//! Create relaxed combined codes, e.g "L2J" -> "L3J", ...
static QStringList alternativeCombinedCodes(const QString &combinedCode);

View File

@@ -301,13 +301,8 @@ namespace BlackMisc
QString CAirlineIcaoCode::normalizeDesignator(const QString candidate)
{
QString n(candidate.trimmed().toUpper());
if (n.contains(' ')) { n = n.left(n.indexOf(' ')); } // cutoff at first space
if (n.isEmpty()) { return n; }
static QThreadStorage<QRegularExpression> tsRegex;
if (! tsRegex.hasLocalData()) { tsRegex.setLocalData(QRegularExpression("[^a-zA-Z\\d\\s]")); }
const QRegularExpression &regexp = tsRegex.localData();
return n.remove(regexp);
n = n.left(indexOfChar(n, [](QChar c) { return c.isSpace(); }));
return removeChars(n, [](QChar c) { return !c.isLetterOrNumber(); });
}
QString CAirlineIcaoCode::getCombinedStringWithKey() const

View File

@@ -31,9 +31,8 @@ namespace BlackMisc
{
QString code = icaoCode.trimmed().toUpper();
if (code.length() != 4) return "";
QRegularExpression reg("^[A-Z0-9]{4}$");
auto match = reg.match(code);
return match.hasMatch() ? code : QString();
if (containsChar(code, [](QChar c) { return !c.isLetterOrNumber(); })) { return ""; }
return code;
}
bool CAirportIcaoCode::isValidIcaoDesignator(const QString &icaoCode)

View File

@@ -32,10 +32,7 @@ namespace BlackMisc
QString CCallsign::unifyCallsign(const QString &callsign)
{
QString unified = callsign.toUpper();
// allow A-Z, 0-9, _, but no spaces
unified = unified.remove(QRegExp("[^A-Z\\d_]"));
return unified;
return removeChars(callsign.toUpper(), [](QChar c) { return !c.isLetterOrNumber() && c != '_'; });
}
const CIcon &CCallsign::convertToIcon(const CCallsign &callsign)
@@ -227,12 +224,7 @@ namespace BlackMisc
bool CCallsign::isValidAircraftCallsign(const QString &callsign)
{
if (callsign.length() < 2 || callsign.length() > 10) { return false; }
// We allow all number callsigns
static QThreadStorage<QRegularExpression> tsRegex;
if (! tsRegex.hasLocalData()) { tsRegex.setLocalData(QRegularExpression("^[A-Z0-9]*$")); }
const QRegularExpression &regexp = tsRegex.localData();
return (regexp.match(callsign).hasMatch());
return !containsChar(callsign, [](QChar c) { return !c.isUpper() && !c.isDigit(); });
}
bool CCallsign::isValidAircraftCallsign(const CCallsign &callsign)
@@ -243,12 +235,7 @@ namespace BlackMisc
bool CCallsign::isValidAtcCallsign(const QString &callsign)
{
if (callsign.length() < 2 || callsign.length() > 10) { return false; }
// We allow all number callsigns
static QThreadStorage<QRegularExpression> tsRegex;
if (! tsRegex.hasLocalData()) { tsRegex.setLocalData(QRegularExpression("^[A-Z0-9_]*$")); }
const QRegularExpression &regexp = tsRegex.localData();
return (regexp.match(callsign).hasMatch());
return !containsChar(callsign, [](QChar c) { return !c.isUpper() && !c.isDigit(); });
}
bool CCallsign::isValidAtcCallsign(const CCallsign &callsign)

View File

@@ -18,6 +18,7 @@
#include "blackmisc/simulation/xplane/aircraftmodelloaderxplane.h"
#include "blackmisc/simulation/xplane/xplaneutil.h"
#include "blackmisc/statusmessage.h"
#include "blackmisc/stringutils.h"
#include "blackmisc/worker.h"
#include <string.h>
@@ -386,7 +387,7 @@ namespace BlackMisc
// Version number.
QString versionLine = readLineFrom(ts);
if (versionLine.isNull()) { return false; }
QString version = versionLine.split(QRegularExpression("\\s"), QString::SkipEmptyParts).at(0);
QString version = splitStringRefs(versionLine, [](QChar c) { return c.isSpace(); }).value(0).toString();
// For version 7, there is another line 'obj'
if (version == "700") { readLineFrom(ts); }
@@ -394,7 +395,7 @@ namespace BlackMisc
// Texture
QString textureLine = readLineFrom(ts);
if (textureLine.isNull()) { return false; }
QString texture = textureLine.split(QRegularExpression("\\s"), QString::SkipEmptyParts).at(0);
QString texture = splitStringRefs(textureLine, [](QChar c) { return c.isSpace(); }).value(0).toString();
objFile.close();
@@ -570,7 +571,7 @@ namespace BlackMisc
{
++lineNum;
QString line = in.readLine();
auto tokens = line.split(QRegularExpression("\\s+"));
auto tokens = splitString(line, [](QChar c) { return c.isSpace(); });
if (!tokens.empty())
{
auto it = commands.find(tokens[0]);
@@ -614,7 +615,7 @@ namespace BlackMisc
{
++lineNum;
QString line = in.readLine();
auto tokens = line.split(QRegularExpression("\\s+"), QString::SkipEmptyParts);
auto tokens = splitString(line, [](QChar c) { return c.isSpace(); });
if (!tokens.empty())
{
auto it = commands.find(tokens[0]);

View File

@@ -16,6 +16,16 @@
namespace BlackMisc
{
QList<QStringRef> splitLinesRefs(const QString &s)
{
return splitStringRefs(s, [](QChar c) { return c == '\n' || c == '\r'; });
}
QStringList splitLines(const QString &s)
{
return splitString(s, [](QChar c) { return c == '\n' || c == '\r'; });
}
QString boolToOnOff(bool v, bool i18n)
{
Q_UNUSED(i18n);
@@ -166,9 +176,7 @@ namespace BlackMisc
QString simplifyNameForSearch(const QString &name)
{
static const QRegularExpression reg("[^A-Z]");
const QString r = name.toUpper().remove(reg);
return r;
return removeChars(name.toUpper(), [](QChar c) { return !c.isUpper(); });
}
}

View File

@@ -13,22 +13,84 @@
#define BLACKMISC_STRINGUTILS_H
#include "blackmisc/blackmiscexport.h"
#include "blackmisc/range.h"
#include <QByteArray>
#include <QDataStream>
#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;
}
//! 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 std::distance(s.begin(), it);
}
//! 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)
{