2

Some languages have ltrim(), rtrim() functions with arbitrary characters to trim, however seems QString in Qt does not have anything similar for now. How to implement such with current functionality? Regexp?

Note. Good if Qt team could add add ltrim(), rtrim() methods in QString() like some other languages have:

QString ltrim(QByteArray &chars); // trim left
QString rtrim(QByteArray &chars); // trim right

and overloaded

trimmed(QByteArray &chars)

to trim arbitrary characters, not only whitespaces. Should be very convenient.

Aleksey Kontsevich
  • 4,671
  • 4
  • 46
  • 101

2 Answers2

3

If you believe the feature is important then you can post a feature request on https://bugreports.qt.io/. If you look at qstring.cpp you can see the function is fairly simple and you can create your own custom function much faster.

You can trim whitespaces, \n, \r etc with QString::simplified();

qDebug() << QString(" lots\t of\nwhitespace\r\n ").simplified();

Output: lots of whitespace

or if you like to trim custom letters, you can use QString::remove with QRegExp:

qDebug() << QString("13Hello").remove(QRegExp("^([0-9]{2})")); // trim 2 numbers at begin
Output: Hello

qDebug() << QString("Hello13").remove(QRegExp("([0-9]{2})$")); // trim 2 numbers at the end
Output: Hello

qDebug() << QString(",./Hello").remove(QRegExp("^([,|.|/]+)")); // trim matched characters at the begin
Output: Hello

qDebug() << QString("Hello,./").remove(QRegExp("([,|.|/]+)$")); // trim matched characters at the end
Output: Hello
Aleksey Kontsevich
  • 4,671
  • 4
  • 46
  • 101
user3606329
  • 2,405
  • 1
  • 16
  • 28
  • Of course `trimmed` can be overloaded with different argument types. That's the typical case for overloading, other than overloading on the type of `this`. – Kuba hasn't forgotten Monica Apr 15 '18 at 22:19
  • What if do not know how many characters to trim? Could You change Your example please? What if if want to trim, for example, all `,`, `/` etc. Would this be correct: `.remove(QRegExp("^([,/]{*})")`? – Aleksey Kontsevich Apr 16 '18 at 18:41
  • About bug report, it is not accepted for now: https://bugreports.qt.io/browse/QTBUG-67712 – Aleksey Kontsevich Apr 16 '18 at 18:57
  • 2
    Aleksey Kontsevich: `.remove(QRegExp(^([,|.|\/|]+));` matches `|` , `,`, `.`, and `/`. `+` means here it will match multiple times. – user3606329 Apr 16 '18 at 20:35
  • Aleksey Kontsevich: Don't worry about rejected bug reports or feature requests. At the moment there 23091 unresolved issues. Even if they accept it, it can take months or years until it is integrated. Better you write it yourself and submit it. – user3606329 Apr 16 '18 at 20:41
  • Thiago Macieira did it once but was rejected: https://bugreports.qt.io/browse/QTBUG-67712?focusedCommentId=399521&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-399521 – Aleksey Kontsevich Apr 16 '18 at 21:14
1

A QByteArray is not a way to store QChars: QString is. Given that, the implementation is not complicated. The switch-over point between using ws.contains and std::binary_search should be selected based on a benchmark.

The trimmedRef, trimmedLeftRef and trimmedRightRef are optimizations that don't copy the source string. They can be used when the trimmed version of the string doesn't outlive the source string - this avoids a copy.

#include <QString>
#include <algorithm>

namespace detail { struct IsSpace final {
  static QString sorted(QString s) {
    std::sort(s.begin(), s.end());
    return s;
  }
  QString const ws;
  QString::const_iterator begin = ws.cBegin(), end = ws.cEnd();
  bool (IsSpace::*test)(QChar) const = 
    ws.isEmpty()     ? &IsSpace::test1 :
    (ws.size() <= 8) ? &IsSpace::test2 :
                       &IsSpace::test3;
  explicit IsSpace(const QString &whitespace) : ws(sorted(whitespace)) {}
  bool test1(const QChar c) const { return c.isSpace(); }
  bool test2(const QChar c) const { return ws.contains(c); }
  bool test3(const QChar c) const { return std::binary_search(begin, end, c); }
  inline bool operator()(QChar c) const { return (*this.*test)(c); }
};  }

enum TrimmedOption { TrimmedLeft = 1, TrimmedRight = 2 };
Q_DECLARE_FLAGS(TrimmedOptions, TrimmedOption)
Q_DECLARE_OPERATORS_FOR_FLAGS(TrimedOptions)
QStringRef trimmedRef(const QString &src, const QString &whitespace,
  TrimmedOptions opt = TrimmedLeft | TrimmedRight) {
  detail::IsSpace const isSpace{whitespace};
  int l = 0;
  if (options & TrimmedLeft)
    while (l < src.length() && isSpace(src[l]))
      l++;
  int r = src.length();
  if (options & TrimmedRight)
    while (r > 0 && isSpace(src[r-1]))
      r--;
  return {&src, l, r-l};
}
QStringRef trimmedLeftRef(const QString &src, const QString &whitespace = {}) {
  return trimmedRef(src, whitespace, TrimmedLeft);
}
QStringRef trimmedRightRef(const QString &src, const QString &whitespace = {}) {
  return trimmed(src, whitespace, TrimmedRight);
}

QString trimmed(const QString &src, const QString &whitespace,
                TrimmedOptions opt = TrimmedLeft | TrimmedRight) {
  return trimmedRef(src, whitespace, opt);
}
QString trimmedLeft(const QString &src, const QString &whitespace = {}) {
  return trimmedRef(src, whitespace, TrimmedLeft);
}
QString trimmedRight(const QString &src, const QString &whitespace = {}) {
  return trimmedRef(src, whitespace, TrimmedRight);
}
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313