C ++:字符串结构的相等性

时间:2014-12-09 20:31:19

标签: c++ string runtime

在我的代码中,我有一个结构,类似于以下内容:

struct basket { 
    std::string a;
    std::string b;
    std::string c;
    std::string d;
}; 

我希望能够比较两个结构并确定> 0,< 0,== 0通过比较所有这些字符串的串联

std::string total = (a+b+c+d);

但是,我希望在不进行实际连接的情况下实现此目的,因为这种比较被多次使用,并且最终成为运行时瓶颈。我知道如果是这样的话,我应该考虑避免使用字符串,但是现在我只想在没有连接的情况下轻松地进行这种比较。

现在,我使用了巨大的if语句。例如,比较结构的每个实例的字符串a,如果它们相同,则比较b,如果它们相同,则比较c,如果它们相同则最后比较d,但我想知道是否存在更简洁的方法在c ++中执行此操作,但没有连续的运行时命中。

谢谢。

4 个答案:

答案 0 :(得分:2)

如果我理解正确,如果字符串的串联相等,你希望两个结构相等,所以

a == "hello", b == "there", ...

匹配

a = "hel", b == "lothere", ...

我会使用boost::range::join执行此操作:

struct basket {
    ...
    bool operator==(const basket& other) const
    {
        using namespace boost::range;
        auto left = join(join(join(a, b), c), d);
        auto right = join(join(join(other.a, other.b), other.c), other.d);
        return equal(left, right); // http://www.boost.org/doc/libs/1_51_0/libs/range/doc/html/range/reference/algorithms/non_mutating/equal.html
    }
};

boost::range::joinhttp://www.boost.org/doc/libs/1_57_0/libs/range/doc/html/range/reference/utilities/join.html)创建连接范围而不进行连接。它内部只是一直迭代。唯一的额外开销是检查第一个范围的结束并移动到第二个范围,因此它应该比实际连接快得多。

更新: 我最初错过了strcmp风格回归的愿望。不幸的是,我无法找到任何返回这样的值的标准算法。不过,在好的方面,写起来很容易。以下是更新版本,compare代替equal

#include <boost/range/join.hpp>

template <typename SinglePassRange1, typename  SinglePassRange2>
int compare(const SinglePassRange1& left, const SinglePassRange2& right)
{
    using namespace std;
    auto leftIt = begin(left);
    auto leftEnd = end(right);
    auto rightIt = begin(right);
    auto rightEnd = end(right);

    for ( ; leftIt != leftEnd 
            && rightIt != rightEnd 
            && *leftIt == *rightIt
          ; ++leftIt, ++rightIt)
    {
    }
    // should be safe since one-past-end for strings is '\0' 
    return static_cast<int>(*leftIt) - static_cast<int>(*rightIt); 
}

struct basket {
    std::string a;
    std::string b;
    std::string c;
    std::string d;

    int compare(const basket& other) const
    {
        using namespace boost::range;
        auto left = join(join(join(a, b), c), d);
        auto right = join(join(join(other.a, other.b), other.c), other.d);

        return ::compare(left, right);
    }
};

在GCC 4.9.1 Ubuntu上进行测试。

compare自由函数应该被称为compareStringRanges或者其他东西,因为该实现只对字符串有效。我将其留给了个人美学领域。

答案 1 :(得分:0)

使用这些字段的简单方法是使用字段数组&#39;存储,而不是单个字段。

您可以创建一个临时数组来简化代码。您的实现几乎肯定会将std::array的数据放在堆栈上。

插图:

  int compare(const basket &pOther) const {
    const BasketStringRefs a(this->allStrings());
    const BasketStringRefs b(pOther.allStrings());

    // ...your evaluation here, involving iteration over a and b...

  }

private:
  typedef std::array<const std::string*, 4> BasketStringRefs;
  BasketStringRefs allStrings() const {
    return BasketStringRefs{{&a, &b, &c, &d}};
  }

答案 2 :(得分:0)

设置会很麻烦,但效率很高:将a,b,c和d存储在一个缓冲区中。您可以维护每个指针以跟踪每个开始的位置(或者如果您知道最大尺寸,则将它们存储在相对位置0,最大尺寸,最大尺寸* 2和最大尺寸+ 3)。

当你比较篮子时,比较它们的缓冲区,使用一个上升到缓冲区大小的for循环而不是一个以null字符结尾的while循环。当你得到一个空字符时,你就完成了那个字符串;继续下一个,看看你要比较的下一个字符。如果这种情况发生了4次,你已经阅读了所有4个,并且应该退出那个篮子。

//baskets are a and b
for (int aIndex = 0, bindex = 0; aIndex < MAX && bIndex < MAX; ++aIndex, ++bIndex)
{
 //if we reach null char for basket a, skip to next string in basket a.  Same for b.

 //if we run out of strings in basket a first, it's shorter 
 //     and comes first in alpha order.  Same for b.

 //if we run out of strings in both baskets without finding any differences, 
 //   return 0 for equal

 //compare the next char in each buffer; if different, return -1 or 1 appropriately

}

答案 3 :(得分:0)

我为连接字符串写了一个快速而又脏的迭代器,只是为了好玩:

#include "stdafx.h"
#include <iostream>
#include <assert.h>

class concatenation {
private:
    std::string a, b, c, d;
public:
    concatenation(){}
    concatenation(const std::string &s1, const std::string &s2, const std::string &s3, const std::string &s4)
        : a(s1), b(s2), c(s3), d(s4)
    {}

    class const_iterator {
    private:
        size_t stringnumber, stringposition;
        const concatenation &target;
    public:
        const_iterator(const concatenation &r, size_t n, size_t p) : target(r), stringnumber(n), stringposition(p) {}
        bool operator ==(const const_iterator &rhs){ return stringnumber == rhs.stringnumber && stringposition == rhs.stringposition; }
        bool operator !=(const const_iterator &rhs){ return !(*this == rhs); }
        char operator *(){
            switch (stringnumber){
            case 0: return target.a[stringposition];
            case 1: return target.b[stringposition];
            case 2: return target.c[stringposition];
            case 3: return target.d[stringposition];
            default:
                return '\0';
            }
        }

        const_iterator& operator ++(){
            size_t current_length;
            switch (stringnumber){
            case 0: current_length = target.a.size(); break;
            case 1: current_length = target.b.size(); break;
            case 2: current_length = target.c.size(); break;
            case 3: current_length = target.d.size(); break;
            default:
                assert(0);
                current_length = 0;
            }
            if (stringposition < current_length-1){
                ++stringposition;
            } else {
                stringposition = 0;
                ++stringnumber;
            }
            return *this; 
        }
    };

    const_iterator begin() const {
        return const_iterator(*this, 0, 0);
    }

    const_iterator end() const {
        return const_iterator(*this, 4, 0);
    }
};

int compare(const concatenation &left, const concatenation &right){
    concatenation::const_iterator p1 (left.begin());
    concatenation::const_iterator p2 (right.begin());
    while (p1 != left.end() && p2 != right.end() && (*p1)==(*p2)){
        ++p1; 
        ++p2;
    }
    if (*p1 != *p2){
        if (p1 == left.end() && p2 != right.end())
            return -1;
        if (p1 != left.end() && p2 == right.end())
            return 1;
        if (*p1 < *p2)
            return -1;
        else
            return 1;
    }
    return 0;
}

int main()
{
    concatenation test("hel", "lo ", "wor", "ld.");
    for (concatenation::const_iterator pos = test.begin(); pos != test.end(); ++pos)
        std::cout << *pos;
    std::cout << std::endl;

    {
        // same
        concatenation first("hello", " ", "world", ".");
        concatenation second("hel", "lo ", "wor", "ld.");
        if (0 != compare(first, second))
            assert(0);
    }

    {
        // first character different
        concatenation first("hello", " ", "world", ".");
        concatenation second("jel", "lo ", "wor", "ld.");
        if (-1 != compare(first, second))
            assert(0);
        if (1 != compare(second, first))
            assert(0);
    }

    {
        // middle character different
        concatenation first("hello", " ", "world", ".");
        concatenation second("hel", "p! ", "wor", "ld.");
        if (-1 != compare(first, second))
            assert(0);
        if (1 != compare(second, first))
            assert(0);
    }

    {
        // length different
        concatenation first("hello", "", "", "");
        concatenation second("hel", "lo ", "wor", "ld.");
        if (-1 != compare(first, second))
            assert(0);
        if (1 != compare(second, first))
            assert(0);
    }

    return 0;
}