diff --git a/src/corelib/text/qregexp.cpp b/src/corelib/text/qregexp.cpp index 758a3695c9..cbf827f2b8 100644 --- a/src/corelib/text/qregexp.cpp +++ b/src/corelib/text/qregexp.cpp @@ -4479,6 +4479,419 @@ int QRegExp::matchedLength() const return priv->matchState.captured[1]; } + +/*! + Replaces every occurrence of this regular expression in + \a str with \a after and returns the result. + + For regular expressions containing \l{capturing parentheses}, + occurrences of \b{\\1}, \b{\\2}, ..., in \a after are replaced + with \a{rx}.cap(1), cap(2), ... + + \sa indexIn(), lastIndexIn(), QRegExp::cap() +*/ +QString QRegExp::replaceIn(const QString &str, const QString &after) const +{ + struct QStringCapture + { + int pos; + int len; + int no; + }; + + QRegExp rx2(*this); + + if (str.isEmpty() && rx2.indexIn(str) == -1) + return str; + + QString s(str); + + int index = 0; + int numCaptures = rx2.captureCount(); + int al = after.length(); + QRegExp::CaretMode caretMode = QRegExp::CaretAtZero; + + if (numCaptures > 0) { + const QChar *uc = after.unicode(); + int numBackRefs = 0; + + for (int i = 0; i < al - 1; i++) { + if (uc[i] == QLatin1Char('\\')) { + int no = uc[i + 1].digitValue(); + if (no > 0 && no <= numCaptures) + numBackRefs++; + } + } + + /* + This is the harder case where we have back-references. + */ + if (numBackRefs > 0) { + QVarLengthArray captures(numBackRefs); + int j = 0; + + for (int i = 0; i < al - 1; i++) { + if (uc[i] == QLatin1Char('\\')) { + int no = uc[i + 1].digitValue(); + if (no > 0 && no <= numCaptures) { + QStringCapture capture; + capture.pos = i; + capture.len = 2; + + if (i < al - 2) { + int secondDigit = uc[i + 2].digitValue(); + if (secondDigit != -1 && ((no * 10) + secondDigit) <= numCaptures) { + no = (no * 10) + secondDigit; + ++capture.len; + } + } + + capture.no = no; + captures[j++] = capture; + } + } + } + + while (index <= s.length()) { + index = rx2.indexIn(s, index, caretMode); + if (index == -1) + break; + + QString after2(after); + for (j = numBackRefs - 1; j >= 0; j--) { + const QStringCapture &capture = captures[j]; + after2.replace(capture.pos, capture.len, rx2.cap(capture.no)); + } + + s.replace(index, rx2.matchedLength(), after2); + index += after2.length(); + + // avoid infinite loop on 0-length matches (e.g., QRegExp("[a-z]*")) + if (rx2.matchedLength() == 0) + ++index; + + caretMode = QRegExp::CaretWontMatch; + } + return s; + } + } + + /* + This is the simple and optimized case where we don't have + back-references. + */ + while (index != -1) { + struct { + int pos; + int length; + } replacements[2048]; + + int pos = 0; + int adjust = 0; + while (pos < 2047) { + index = rx2.indexIn(s, index, caretMode); + if (index == -1) + break; + int ml = rx2.matchedLength(); + replacements[pos].pos = index; + replacements[pos++].length = ml; + index += ml; + adjust += al - ml; + // avoid infinite loop + if (!ml) + index++; + } + if (!pos) + break; + replacements[pos].pos = s.size(); + int newlen = s.size() + adjust; + + // to continue searching at the right position after we did + // the first round of replacements + if (index != -1) + index += adjust; + QString newstring; + newstring.reserve(newlen + 1); + QChar *newuc = newstring.data(); + QChar *uc = newuc; + int copystart = 0; + int i = 0; + while (i < pos) { + int copyend = replacements[i].pos; + int size = copyend - copystart; + memcpy(static_cast(uc), static_cast(s.constData() + copystart), size * sizeof(QChar)); + uc += size; + memcpy(static_cast(uc), static_cast(after.constData()), al * sizeof(QChar)); + uc += al; + copystart = copyend + replacements[i].length; + i++; + } + memcpy(static_cast(uc), static_cast(s.constData() + copystart), (s.size() - copystart) * sizeof(QChar)); + newstring.resize(newlen); + s = newstring; + caretMode = QRegExp::CaretWontMatch; + } + return s; + +} + + +/*! + \fn QString QRegExp::removeIn(const QString &str) + + Removes every occurrence of this regular expression \a str, and + returns the result + + Does the same as replaceIn(str, QString()). + + \sa indexIn(), lastIndexIn(), replaceIn() +*/ + + +/*! + \fn QString QRegExp::countIn(const QString &str) + + Returns the number of times this regular expression matches + in \a str. + + \sa indexIn(), lastIndexIn(), replaceIn() +*/ + +int QRegExp::countIn(const QString &str) const +{ + QRegExp rx2(*this); + int count = 0; + int index = -1; + int len = str.length(); + while (index < len - 1) { // count overlapping matches + index = rx2.indexIn(str, index + 1); + if (index == -1) + break; + count++; + } + return count; +} + +class qt_section_chunk { +public: + qt_section_chunk() {} + qt_section_chunk(int l, QStringRef s) : length(l), string(std::move(s)) {} + int length; + QStringRef string; +}; + +static QString extractSections(const QVector §ions, + int start, + int end, + QString::SectionFlags flags) +{ + const int sectionsSize = sections.size(); + + if (!(flags & QString::SectionSkipEmpty)) { + if (start < 0) + start += sectionsSize; + if (end < 0) + end += sectionsSize; + } else { + int skip = 0; + for (int k = 0; k < sectionsSize; ++k) { + const qt_section_chunk §ion = sections.at(k); + if (section.length == section.string.length()) + skip++; + } + if (start < 0) + start += sectionsSize - skip; + if (end < 0) + end += sectionsSize - skip; + } + if (start >= sectionsSize || end < 0 || start > end) + return QString(); + + QString ret; + int x = 0; + int first_i = start, last_i = end; + for (int i = 0; x <= end && i < sectionsSize; ++i) { + const qt_section_chunk §ion = sections.at(i); + const bool empty = (section.length == section.string.length()); + if (x >= start) { + if (x == start) + first_i = i; + if (x == end) + last_i = i; + if (x != start) + ret += section.string; + else + ret += section.string.mid(section.length); + } + if (!empty || !(flags & QString::SectionSkipEmpty)) + x++; + } + + if ((flags & QString::SectionIncludeLeadingSep) && first_i >= 0) { + const qt_section_chunk §ion = sections.at(first_i); + ret.prepend(section.string.left(section.length)); + } + + if ((flags & QString::SectionIncludeTrailingSep) + && last_i < sectionsSize - 1) { + const qt_section_chunk §ion = sections.at(last_i+1); + ret += section.string.left(section.length); + } + + return ret; +} +/*! + \a str is treated as a sequence of fields separated by this + regular expression. + + \sa splitString() +*/ +QString QRegExp::sectionIn(const QString &str, int start, int end, QString::SectionFlags flags) const +{ + if (str.isEmpty()) + return str; + + QRegExp sep(*this); + sep.setCaseSensitivity((flags & QString::SectionCaseInsensitiveSeps) ? Qt::CaseInsensitive + : Qt::CaseSensitive); + + QVector sections; + int n = str.length(), m = 0, last_m = 0, last_len = 0; + while ((m = sep.indexIn(str, m)) != -1) { + sections.append(qt_section_chunk(last_len, QStringRef(&str, last_m, m - last_m))); + last_m = m; + last_len = sep.matchedLength(); + m += qMax(sep.matchedLength(), 1); + } + sections.append(qt_section_chunk(last_len, QStringRef(&str, last_m, n - last_m))); + + return extractSections(sections, start, end, flags); +} + +/*! + Splits \a str into substrings wherever this regular expression + matches, and returns the list of those strings. If this regular + expression does not match anywhere in the string, split() returns a + single-element list containing \a str. + + \sa QStringList::join(), section(), QString::split() +*/ +QStringList QRegExp::splitString(const QString &str, Qt::SplitBehavior behavior) const +{ + QRegExp rx2(*this); + QStringList list; + int start = 0; + int extra = 0; + int end; + while ((end = rx2.indexIn(str, start + extra)) != -1) { + int matchedLen = rx2.matchedLength(); + if (start != end || behavior == Qt::KeepEmptyParts) + list.append(str.mid(start, end - start)); + start = end + matchedLen; + extra = (matchedLen == 0) ? 1 : 0; + } + if (start != str.size() || behavior == Qt::KeepEmptyParts) + list.append(str.mid(start, -1)); + return list; +} + +/*! + Splits \a str into substrings wherever this regular expression + matches, and returns the list of those strings. If this regular + expression does not match anywhere in the string, split() returns a + single-element list containing \a str. + + \sa QStringList::join(), section(), QString::split() +*/ +QVector QRegExp::splitStringAsRef(const QString &str, Qt::SplitBehavior behavior) const +{ + QRegExp rx2(*this); + QVector list; + int start = 0; + int extra = 0; + int end; + while ((end = rx2.indexIn(str, start + extra)) != -1) { + int matchedLen = rx2.matchedLength(); + if (start != end || behavior == Qt::KeepEmptyParts) + list.append(str.midRef(start, end - start)); + start = end + matchedLen; + extra = (matchedLen == 0) ? 1 : 0; + } + if (start != str.size() || behavior == Qt::KeepEmptyParts) + list.append(str.midRef(start, -1)); + return list; +} + +/*! + \fn QStringList QStringList::filter(const QRegExp &rx) const + + \overload + + Returns a list of all the strings that match the regular + expression \a rx. +*/ +QStringList QRegExp::filterList(const QStringList &stringList) const +{ + QStringList res; + for (const QString &s : stringList) { + if (containedIn(s)) + res << s; + } + return res; +} + +/*! + Replaces every occurrence of the regexp \a rx, in each of the + string lists's strings, with \a after. Returns a reference to the + string list. +*/ +QStringList QRegExp::replaceIn(const QStringList &stringList, const QString &after) const +{ + QStringList list; + for (const QString &s : stringList) + list << replaceIn(s, after); + return list; +} + +/*! + Returns the index position of the first exact match of this regexp in + \a list, searching forward from index position \a from. Returns + -1 if no item matched. + + \sa lastIndexIn(), contains(), exactMatch() +*/ +int QRegExp::indexIn(const QStringList &list, int from) +{ + if (from < 0) + from = qMax(from + list.size(), 0); + for (int i = from; i < list.size(); ++i) { + if (exactMatch(list.at(i))) + return i; + } + return -1; +} + +/*! + Returns the index position of the last exact match of this regexp in + \a list, searching backward from index position \a from. If \a + from is -1 (the default), the search starts at the last item. + Returns -1 if no item matched. + + \sa indexOf(), contains(), QRegExp::exactMatch() +*/ +int QRegExp::lastIndexIn(const QStringList &list, int from) +{ + if (from < 0) + from += list.size(); + else if (from >= list.size()) + from = list.size() - 1; + for (int i = from; i >= 0; --i) { + if (exactMatch(list.at(i))) + return i; + } + return -1; +} + #ifndef QT_NO_REGEXP_CAPTURE /*! @@ -4637,6 +5050,7 @@ QString QRegExp::errorString() { return const_cast(this)->errorString(); } + #endif /*! diff --git a/src/corelib/text/qregexp.h b/src/corelib/text/qregexp.h index b42214f1db..d1181d6fa6 100644 --- a/src/corelib/text/qregexp.h +++ b/src/corelib/text/qregexp.h @@ -108,6 +108,22 @@ public: QString errorString(); #endif + QString replaceIn(const QString &str, const QString &after) const; + QString removeIn(const QString &str) const + { return replaceIn(str, QString()); } + bool containedIn(const QString &str) const + { return indexIn(str) != -1; } + int countIn(const QString &str) const; + QString sectionIn(const QString &str, int start, int end, QString::SectionFlags flags) const; + + QStringList splitString(const QString &str, Qt::SplitBehavior behavior = Qt::KeepEmptyParts) const; + QVector splitStringAsRef(const QString &str, Qt::SplitBehavior behavior = Qt::KeepEmptyParts) const; + + int indexIn(const QStringList &list, int from); + int lastIndexIn(const QStringList &list, int from); + QStringList replaceIn(const QStringList &stringList, const QString &after) const; + QStringList filterList(const QStringList &stringList) const; + static QString escape(const QString &str); friend Q_CORE_EXPORT size_t qHash(const QRegExp &key, size_t seed) noexcept; diff --git a/src/corelib/text/qstring.cpp b/src/corelib/text/qstring.cpp index 72ea8015ae..6ff42129b3 100644 --- a/src/corelib/text/qstring.cpp +++ b/src/corelib/text/qstring.cpp @@ -3918,138 +3918,7 @@ Q_DECLARE_TYPEINFO(QStringCapture, Q_PRIMITIVE_TYPE); */ QString& QString::replace(const QRegExp &rx, const QString &after) { - QRegExp rx2(rx); - - if (isEmpty() && rx2.indexIn(*this) == -1) - return *this; - - reallocData(uint(d.size) + 1u); - - int index = 0; - int numCaptures = rx2.captureCount(); - int al = after.length(); - QRegExp::CaretMode caretMode = QRegExp::CaretAtZero; - - if (numCaptures > 0) { - const QChar *uc = after.unicode(); - int numBackRefs = 0; - - for (int i = 0; i < al - 1; i++) { - if (uc[i] == QLatin1Char('\\')) { - int no = uc[i + 1].digitValue(); - if (no > 0 && no <= numCaptures) - numBackRefs++; - } - } - - /* - This is the harder case where we have back-references. - */ - if (numBackRefs > 0) { - QVarLengthArray captures(numBackRefs); - int j = 0; - - for (int i = 0; i < al - 1; i++) { - if (uc[i] == QLatin1Char('\\')) { - int no = uc[i + 1].digitValue(); - if (no > 0 && no <= numCaptures) { - QStringCapture capture; - capture.pos = i; - capture.len = 2; - - if (i < al - 2) { - int secondDigit = uc[i + 2].digitValue(); - if (secondDigit != -1 && ((no * 10) + secondDigit) <= numCaptures) { - no = (no * 10) + secondDigit; - ++capture.len; - } - } - - capture.no = no; - captures[j++] = capture; - } - } - } - - while (index <= length()) { - index = rx2.indexIn(*this, index, caretMode); - if (index == -1) - break; - - QString after2(after); - for (j = numBackRefs - 1; j >= 0; j--) { - const QStringCapture &capture = captures[j]; - after2.replace(capture.pos, capture.len, rx2.cap(capture.no)); - } - - replace(index, rx2.matchedLength(), after2); - index += after2.length(); - - // avoid infinite loop on 0-length matches (e.g., QRegExp("[a-z]*")) - if (rx2.matchedLength() == 0) - ++index; - - caretMode = QRegExp::CaretWontMatch; - } - return *this; - } - } - - /* - This is the simple and optimized case where we don't have - back-references. - */ - while (index != -1) { - struct { - int pos; - int length; - } replacements[2048]; - - int pos = 0; - int adjust = 0; - while (pos < 2047) { - index = rx2.indexIn(*this, index, caretMode); - if (index == -1) - break; - int ml = rx2.matchedLength(); - replacements[pos].pos = index; - replacements[pos++].length = ml; - index += ml; - adjust += al - ml; - // avoid infinite loop - if (!ml) - index++; - } - if (!pos) - break; - replacements[pos].pos = d.size; - int newlen = d.size + adjust; - - // to continue searching at the right position after we did - // the first round of replacements - if (index != -1) - index += adjust; - QString newstring; - newstring.reserve(newlen + 1); - QChar *newuc = newstring.data(); - QChar *uc = newuc; - int copystart = 0; - int i = 0; - while (i < pos) { - int copyend = replacements[i].pos; - int size = copyend - copystart; - memcpy(static_cast(uc), static_cast(d.data() + copystart), size * sizeof(QChar)); - uc += size; - memcpy(static_cast(uc), static_cast(after.d.data()), al * sizeof(QChar)); - uc += al; - copystart = copyend + replacements[i].length; - i++; - } - memcpy(static_cast(uc), static_cast(d.data() + copystart), (d.size - copystart) * sizeof(QChar)); - newstring.resize(newlen); - *this = newstring; - caretMode = QRegExp::CaretWontMatch; - } + *this = rx.replaceIn(*this, after); return *this; } #endif @@ -4402,17 +4271,7 @@ int QString::lastIndexOf(QRegExp& rx, int from) const */ int QString::count(const QRegExp& rx) const { - QRegExp rx2(rx); - int count = 0; - int index = -1; - int len = length(); - while (index < len - 1) { // count overlapping matches - index = rx2.indexIn(*this, index + 1); - if (index == -1) - break; - count++; - } - return count; + return rx.countIn(*this); } #endif // QT_NO_REGEXP @@ -4756,25 +4615,7 @@ static QString extractSections(const QVector §ions, */ QString QString::section(const QRegExp ®, int start, int end, SectionFlags flags) const { - const QChar *uc = unicode(); - if(!uc) - return QString(); - - QRegExp sep(reg); - sep.setCaseSensitivity((flags & SectionCaseInsensitiveSeps) ? Qt::CaseInsensitive - : Qt::CaseSensitive); - - QVector sections; - int n = length(), m = 0, last_m = 0, last_len = 0; - while ((m = sep.indexIn(*this, m)) != -1) { - sections.append(qt_section_chunk(last_len, QStringRef(this, last_m, m - last_m))); - last_m = m; - last_len = sep.matchedLength(); - m += qMax(sep.matchedLength(), 1); - } - sections.append(qt_section_chunk(last_len, QStringRef(this, last_m, n - last_m))); - - return extractSections(sections, start, end, flags); + return reg.sectionIn(*this, start, end, flags); } #endif @@ -7742,28 +7583,6 @@ QVector QStringRef::split(QChar sep, QString::SplitBehavior behavior #endif #ifndef QT_NO_REGEXP -namespace { -template -static ResultList splitString(const QString &source, MidMethod mid, const QRegExp &rx, Qt::SplitBehavior behavior) -{ - QRegExp rx2(rx); - ResultList list; - int start = 0; - int extra = 0; - int end; - while ((end = rx2.indexIn(source, start + extra)) != -1) { - int matchedLen = rx2.matchedLength(); - if (start != end || behavior == Qt::KeepEmptyParts) - list.append((source.*mid)(start, end - start)); - start = end + matchedLen; - extra = (matchedLen == 0) ? 1 : 0; - } - if (start != source.size() || behavior == Qt::KeepEmptyParts) - list.append((source.*mid)(start, -1)); - return list; -} -} // namespace - /*! \overload \since 5.14 @@ -7793,7 +7612,7 @@ static ResultList splitString(const QString &source, MidMethod mid, const QRegEx */ QStringList QString::split(const QRegExp &rx, Qt::SplitBehavior behavior) const { - return splitString(*this, &QString::mid, rx, behavior); + return rx.splitString(*this, behavior); } # if QT_DEPRECATED_SINCE(5, 15) @@ -7803,7 +7622,7 @@ QStringList QString::split(const QRegExp &rx, Qt::SplitBehavior behavior) const */ QStringList QString::split(const QRegExp &rx, SplitBehavior behavior) const { - return split(rx, mapSplitBehavior(behavior)); + return rx.splitString(*this, mapSplitBehavior(behavior)); } # endif @@ -7823,7 +7642,7 @@ QStringList QString::split(const QRegExp &rx, SplitBehavior behavior) const */ QVector QString::splitRef(const QRegExp &rx, Qt::SplitBehavior behavior) const { - return splitString >(*this, &QString::midRef, rx, behavior); + return rx.splitStringAsRef(*this, behavior); } # if QT_DEPRECATED_SINCE(5, 15) @@ -7834,7 +7653,7 @@ QVector QString::splitRef(const QRegExp &rx, Qt::SplitBehavior behav */ QVector QString::splitRef(const QRegExp &rx, SplitBehavior behavior) const { - return splitRef(rx, mapSplitBehavior(behavior)); + return rx.splitStringAsRef(*this, mapSplitBehavior(behavior)); } # endif #endif // QT_NO_REGEXP diff --git a/src/corelib/text/qstringlist.cpp b/src/corelib/text/qstringlist.cpp index 4b9dcee169..57abfb8f7d 100644 --- a/src/corelib/text/qstringlist.cpp +++ b/src/corelib/text/qstringlist.cpp @@ -460,11 +460,7 @@ bool QtPrivate::QStringList_contains(const QStringList *that, QLatin1String str, */ QStringList QtPrivate::QStringList_filter(const QStringList *that, const QRegExp &rx) { - QStringList res; - for (int i = 0; i < that->size(); ++i) - if (that->at(i).contains(rx)) - res << that->at(i); - return res; + return rx.filterList(*that); } #endif @@ -566,8 +562,7 @@ void QtPrivate::QStringList_replaceInStrings(QStringList *that, const QString &b */ void QtPrivate::QStringList_replaceInStrings(QStringList *that, const QRegExp &rx, const QString &after) { - for (int i = 0; i < that->size(); ++i) - (*that)[i].replace(rx, after); + *that = rx.replaceIn(*that, after); } #endif @@ -716,30 +711,6 @@ QString QtPrivate::QStringList_join(const QStringList *that, QStringView sep) */ #ifndef QT_NO_REGEXP -static int indexOfMutating(const QStringList *that, QRegExp &rx, int from) -{ - if (from < 0) - from = qMax(from + that->size(), 0); - for (int i = from; i < that->size(); ++i) { - if (rx.exactMatch(that->at(i))) - return i; - } - return -1; -} - -static int lastIndexOfMutating(const QStringList *that, QRegExp &rx, int from) -{ - if (from < 0) - from += that->size(); - else if (from >= that->size()) - from = that->size() - 1; - for (int i = from; i >= 0; --i) { - if (rx.exactMatch(that->at(i))) - return i; - } - return -1; -} - /*! \fn int QStringList::indexOf(const QRegExp &rx, int from) const @@ -752,7 +723,7 @@ static int lastIndexOfMutating(const QStringList *that, QRegExp &rx, int from) int QtPrivate::QStringList_indexOf(const QStringList *that, const QRegExp &rx, int from) { QRegExp rx2(rx); - return indexOfMutating(that, rx2, from); + return rx2.indexIn(*that, from); } /*! @@ -771,7 +742,7 @@ int QtPrivate::QStringList_indexOf(const QStringList *that, const QRegExp &rx, i */ int QtPrivate::QStringList_indexOf(const QStringList *that, QRegExp &rx, int from) { - return indexOfMutating(that, rx, from); + return rx.indexIn(*that, from); } /*! @@ -787,7 +758,7 @@ int QtPrivate::QStringList_indexOf(const QStringList *that, QRegExp &rx, int fro int QtPrivate::QStringList_lastIndexOf(const QStringList *that, const QRegExp &rx, int from) { QRegExp rx2(rx); - return lastIndexOfMutating(that, rx2, from); + return rx2.lastIndexIn(*that, from); } /*! @@ -807,7 +778,7 @@ int QtPrivate::QStringList_lastIndexOf(const QStringList *that, const QRegExp &r */ int QtPrivate::QStringList_lastIndexOf(const QStringList *that, QRegExp &rx, int from) { - return lastIndexOfMutating(that, rx, from); + return rx.lastIndexIn(*that, from); } #endif diff --git a/tests/auto/corelib/text/qregexp/tst_qregexp.cpp b/tests/auto/corelib/text/qregexp/tst_qregexp.cpp index a8111af6c1..d1dad01abb 100644 --- a/tests/auto/corelib/text/qregexp/tst_qregexp.cpp +++ b/tests/auto/corelib/text/qregexp/tst_qregexp.cpp @@ -69,6 +69,20 @@ private slots: void validityCheck_data(); void validityCheck(); void escapeSequences(); + + void splitString_data(); + void splitString(); + + void countIn(); + void containedIn(); + + void replaceIn_data(); + void replaceIn(); + void removeIn_data(); + void removeIn(); + + void filterList(); + void replaceInList(); }; // Testing get/set functions @@ -1381,6 +1395,155 @@ void tst_QRegExp::escapeSequences() } } +void tst_QRegExp::splitString_data() +{ + QTest::addColumn("string"); + QTest::addColumn("pattern"); + QTest::addColumn("result"); + + QTest::newRow("data01") << "Some text\n\twith strange whitespace." + << "\\s+" + << (QStringList() << "Some" << "text" << "with" << "strange" << "whitespace." ); + + QTest::newRow("data02") << "This time, a normal English sentence." + << "\\W+" + << (QStringList() << "This" << "time" << "a" << "normal" << "English" << "sentence" << ""); + + QTest::newRow("data03") << "Now: this sentence fragment." + << "\\b" + << (QStringList() << "" << "Now" << ": " << "this" << " " << "sentence" << " " << "fragment" << "."); +} + +void tst_QRegExp::splitString() +{ + QFETCH(QString, string); + QFETCH(QString, pattern); + QFETCH(QStringList, result); + QStringList list = QRegExp(pattern).splitString(string); + QVERIFY(list == result); + + QVERIFY(list == result); + + result.removeAll(QString()); + + list = QRegExp(pattern).splitString(string, Qt::SkipEmptyParts); + QVERIFY(list == result); +} + +void tst_QRegExp::countIn() +{ + QString a; + a="ABCDEFGHIEfGEFG"; // 15 chars + QCOMPARE(QRegExp("[FG][HI]").countIn(a),1); + QCOMPARE(QRegExp("[G][HE]").countIn(a),2); +} + + +void tst_QRegExp::containedIn() +{ + QString a; + a="ABCDEFGHIEfGEFG"; // 15 chars + QVERIFY(QRegExp("[FG][HI]").containedIn(a)); + QVERIFY(QRegExp("[G][HE]").containedIn(a)); +} + +void tst_QRegExp::replaceIn_data() +{ + QTest::addColumn("string" ); + QTest::addColumn("regexp" ); + QTest::addColumn("after" ); + QTest::addColumn("result" ); + + QTest::newRow( "rem00" ) << QString("alpha") << QString("a+") << QString("") << QString("lph"); + QTest::newRow( "rem01" ) << QString("banana") << QString("^.a") << QString("") << QString("nana"); + QTest::newRow( "rem02" ) << QString("") << QString("^.a") << QString("") << QString(""); + QTest::newRow( "rem03" ) << QString("") << QString("^.a") << QString() << QString(""); + QTest::newRow( "rem04" ) << QString() << QString("^.a") << QString("") << QString(); + QTest::newRow( "rem05" ) << QString() << QString("^.a") << QString() << QString(); + + QTest::newRow( "rep00" ) << QString("A bon mot.") << QString("([^<]*)") << QString("\\emph{\\1}") << QString("A \\emph{bon mot}."); + QTest::newRow( "rep01" ) << QString("banana") << QString("^.a()") << QString("\\1") << QString("nana"); + QTest::newRow( "rep02" ) << QString("banana") << QString("(ba)") << QString("\\1X\\1") << QString("baXbanana"); + QTest::newRow( "rep03" ) << QString("banana") << QString("(ba)(na)na") << QString("\\2X\\1") << QString("naXba"); + + QTest::newRow("backref00") << QString("\\1\\2\\3\\4\\5\\6\\7\\8\\9\\A\\10\\11") << QString("\\\\[34]") + << QString("X") << QString("\\1\\2XX\\5\\6\\7\\8\\9\\A\\10\\11"); + QTest::newRow("backref01") << QString("foo") << QString("[fo]") << QString("\\1") << QString("\\1\\1\\1"); + QTest::newRow("backref02") << QString("foo") << QString("([fo])") << QString("(\\1)") << QString("(f)(o)(o)"); + QTest::newRow("backref03") << QString("foo") << QString("([fo])") << QString("\\2") << QString("\\2\\2\\2"); + QTest::newRow("backref04") << QString("foo") << QString("([fo])") << QString("\\10") << QString("f0o0o0"); + QTest::newRow("backref05") << QString("foo") << QString("([fo])") << QString("\\11") << QString("f1o1o1"); + QTest::newRow("backref06") << QString("foo") << QString("([fo])") << QString("\\19") << QString("f9o9o9"); + QTest::newRow("backref07") << QString("foo") << QString("(f)(o+)") + << QString("\\2\\1\\10\\20\\11\\22\\19\\29\\3") + << QString("ooff0oo0f1oo2f9oo9\\3"); + QTest::newRow("backref08") << QString("abc") << QString("(((((((((((((([abc]))))))))))))))") + << QString("{\\14}") << QString("{a}{b}{c}"); + QTest::newRow("backref09") << QString("abcdefghijklmn") + << QString("(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)(m)(n)") + << QString("\\19\\18\\17\\16\\15\\14\\13\\12\\11\\10" + "\\9\\90\\8\\80\\7\\70\\6\\60\\5\\50\\4\\40\\3\\30\\2\\20\\1") + << QString("a9a8a7a6a5nmlkjii0hh0gg0ff0ee0dd0cc0bb0a"); + QTest::newRow("backref10") << QString("abc") << QString("((((((((((((((abc))))))))))))))") + << QString("\\0\\01\\011") << QString("\\0\\01\\011"); + QTest::newRow("invalid") << QString("") << QString("invalid regex\\") << QString("") << QString(""); +} + +void tst_QRegExp::replaceIn() +{ + QFETCH( QString, string ); + QFETCH( QString, regexp ); + QFETCH( QString, after ); + + QString s2 = string; + s2 = QRegExp(regexp).replaceIn(s2, after); + QTEST( s2, "result" ); + s2 = string; +} + +void tst_QRegExp::removeIn_data() +{ + replaceIn_data(); +} + +void tst_QRegExp::removeIn() +{ + QFETCH( QString, string ); + QFETCH( QString, regexp ); + QFETCH( QString, after ); + + if ( after.length() == 0 ) { + QString s2 = string; + s2 = QRegExp(regexp).removeIn(s2); + QTEST( s2, "result" ); + } else { + QCOMPARE( 0, 0 ); // shut Qt Test + } +} + +void tst_QRegExp::filterList() +{ + QStringList list3, list4; + list3 << "Bill Gates" << "Joe Blow" << "Bill Clinton"; + list3 = QRegExp("[i]ll") .filterList(list3); + list4 << "Bill Gates" << "Bill Clinton"; + QCOMPARE( list3, list4 ); +} + +void tst_QRegExp::replaceInList() +{ + QStringList list3, list4; + list3 << "alpha" << "beta" << "gamma" << "epsilon"; + list3 = QRegExp("^a").replaceIn(list3, "o"); + list4 << "olpha" << "beta" << "gamma" << "epsilon"; + QCOMPARE( list3, list4 ); + + QStringList list5, list6; + list5 << "Bill Clinton" << "Gates, Bill"; + list6 << "Bill Clinton" << "Bill Gates"; + list5 = QRegExp("^(.*), (.*)$").replaceIn(list5, "\\2 \\1"); + QCOMPARE( list5, list6 ); +} QTEST_APPLESS_MAIN(tst_QRegExp) #include "tst_qregexp.moc"