【问题标题】:How to count recurring characters at the beginning of a QString?如何计算 QString 开头的重复字符?
【发布时间】:2019-02-03 11:36:47
【问题描述】:

我正在处理一个行列表,我需要计算开头出现的哈希值。

#  item 1
## item 1, 1
## item 1, 2
#  item 2

等等。

如果每一行都是一个QString,我如何返回字符串开头出现的哈希数?

QString s("### foo # bar ");
int numberOfHashes = s.count("#"); // Answer should be 3, not 4

【问题讨论】:

    标签: c++ qt qt5 qstring qregularexpression


    【解决方案1】:

    琐碎:

    int number_of_hashes(const QString &s) {
        int i, l = s.size();
        for(i = 0; i < l && s[i] == '#'; ++i);
        return i;
    }
    

    在其他语言(主要是解释性语言)中,您不得不担心迭代字符,因为它很慢,并将所有内容委托给库函数(通常用 C 编写)。在 C++ 中,迭代在性能方面非常好,所以一个脚踏实地的for 循环就可以了。


    只是为了好玩,我将 a small benchmark 与 OP 中的 QRegularExpression 进行了比较,可能缓存了 RE 对象。

    #include <QCoreApplication>
    #include <QString>
    #include <vector>
    #include <QElapsedTimer>
    #include <stdlib.h>
    #include <iostream>
    #include <QRegularExpression>
    
    int number_of_hashes(const QString &s) {
        int i, l = s.size();
        for(i = 0; i < l && s[i] == '#'; ++i);
        return i;
    }
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
        const int count = 100000;
        std::vector<QString> ss;
        for(int i = 0; i < 100; ++i) ss.push_back(QString(rand() % 10, '#') + " foo ## bar ###");
        QElapsedTimer t;
        t.start();
        unsigned tot = 0;
        for(int i = 0; i < count; ++i) {
            for(const QString &s: ss) tot += number_of_hashes(s);
        }
        std::cerr<<"plain loop: "<<t.elapsed()*1000./count<<" ns\n";
        t.restart();
        for(int i = 0; i < count; ++i) {
            for(const QString &s: ss) tot += QRegularExpression("^[#]*").match(s).capturedLength();
        }
        std::cerr<<"QRegularExpression, rebuilt every time: "<<t.elapsed()*1000./count<<" ns\n";
    
        QRegularExpression re("^[#]*");
        t.restart();
        for(int i = 0; i < count; ++i) {
            for(const QString &s: ss) tot += re.match(s).capturedLength();
        }
        std::cerr<<"QRegularExpression, cached: "<<t.elapsed()*1000./count<<" ns\n";
        return tot;    
    }
    

    正如预期的那样,基于QRegularExpression 的速度要慢两个数量级

    plain loop: 0.7 ns
    QRegularExpression, rebuilt every time: 75.66 ns
    QRegularExpression, cached: 24.5 ns
    

    【讨论】:

    • 能否请您也将我的想法添加到基准测试中。你知道,纯粹出于好奇,只是为了好玩。
    【解决方案2】:

    这里我使用标准算法find_if_not 来获取第一个不是哈希的字符的迭代器。然后我返回从字符串开头到该迭代器的距离。

    int number_of_hashes(QString const& s)
    {
        auto it = std::find_if_not(std::begin(s), std::end(s), [](QChar c){return c == '#';});
        return std::distance(std::begin(s), it);
    }
    

    编辑find_if_not 函数只接受一元谓词,而不是值,因此您必须传递一个 lambda 谓词。

    【讨论】:

      【解决方案3】:
      int numberOfHashes = 0;
      int size = s.size();
      QChar ch('#');
      for(int i = 0; (i < size) && (s[i] == ch); ++i) {
          ++numberOfHashes;
      } 
      

      【讨论】:

        【解决方案4】:

        没有for-loop的解决方案

        QString s("### foo # bar ");
        int numberOfHashes = QRegularExpression("^[#]*").match(s).capturedLength();
        

        【讨论】:

        • 为这样一个简单的问题调用正则表达式的愤怒可能是矫枉过正。
        • 与普通的 ad-hoc 循环相比,这可能慢了几个数量级。这在您的用例中是否重要,只有您知道,但是,鉴于“手动”解决方案的长度/编写工作量相似并且速度快几个数量级,我什至不会考虑替代方案;对我们今天拥有的处理能力的草率滥用可能是大多数软件总是很慢的原因,即使硬件变得更快。不过,如果您在多行上执行此操作,至少缓存 QRegularExpression 对象!
        • @Akiva:我在答案中添加了一个小基准;正如预期的那样,您的解决方案慢了约 100 倍。
        【解决方案5】:

        另一种方式:

        int beginsWithCount(const QString &s, const QChar c) {
          int n = 0;
          for (auto ch : s)
            if (c == ch) n++; else break;
          return n;
        }
        

        【讨论】:

          【解决方案6】:

          一种Qt方法,利用QString::indexOf(..):

          QString s("### foo # bar ");
          int numHashes = 0;
          
          while ((numHashes = s.indexOf("#", numHashes)) == numHashes) {
              ++numHashes;
          } // numHashes == 3
          
          int QString::indexOf(const QString &str, int from = 0, 
                               Qt::CaseSensitivity cs = Qt::CaseSensitive) const
          

          返回字符串str在此字符串中第一次出现的索引位置,从索引位置from开始向前搜索。如果找不到str,则返回-1

          从索引0 开始,在字符串s 中搜索# 的第一个匹配项,然后使用谓词测试该匹配项是否在索引0 处。如果未终止,则继续索引1,依此类推。

          但是,这不会使最终的可能完整的字符串搜索短路。如果未在其预期位置找到哈希,则在最终失败的谓词检查之前,将一次完整地搜索字符串(或直到第一个哈希位于错误位置)。

          【讨论】:

          • 搜索是不必要的:我们只关心开头的字符。搜索将成本从 O(1) 提高到 O(N),是一种悲观化。
          • @KubaOber 正如我在回答的最后一段中已经明确指出的那样。我仍然认为QString 的方法在这个问答中可以有一个优点,自然包括最后一段。
          猜你喜欢
          • 1970-01-01
          • 2021-12-28
          • 2016-01-13
          • 1970-01-01
          • 2020-07-12
          • 1970-01-01
          • 2016-05-20
          • 2019-01-11
          • 2018-01-09
          相关资源
          最近更新 更多