Черты, не учитывающие пробелы в начале и конце, для basic_string - PullRequest
0 голосов
/ 28 октября 2018

Я много занимаюсь парсингом / обработкой, где задаются пробелы в начале / в конце и нечувствительность к регистру.Поэтому я сделал базовую черту характера для std::basic_string (см. Ниже), чтобы сэкономить себе немного работы.

Черта не работает, проблема в том, что сравнение basic_string вызывает сравнение черт и при оценке0 возвращает разницу в размерах.В basic_string.h написано ... Если результат сравнения не равен нулю, возвращает его, в противном случае сначала упорядочивается более короткий. Похоже, они явно не хотят, чтобы я это делал ...

Какова причина того, что этот дополнительный "более короткий" порядок упорядочивается, если сравнение черты возвращает 0?И есть ли обходной путь, или я должен свернуть свою собственную строку?

#include <cstring>
#include <iostream>

namespace csi{
template<typename T>
struct char_traits : std::char_traits<T>
{
    static int compare(T const*s1, T const*s2, size_t n){
        size_t n1(n);
        while(n1>0&&std::isspace(*s1))
            ++s1, --n1;
        while(n1>0&&std::isspace(s1[n1-1]))
            --n1;
        size_t n2(n);
        while(n2>0&&std::isspace(*s2))
            ++s2, --n2;
        while(n2>0&&std::isspace(s2[n2-1]))
            --n2;
        return strncasecmp(static_cast<char const*>(s1),
                           static_cast<char const*>(s2),
                           std::min(n1,n2));
    }
};
using string = std::basic_string<char,char_traits<char>>;
}

int main()
{
    using namespace csi;
    string s1 = "hello";
    string s2 = " HElLo ";
    std::cout << std::boolalpha
              << "s1==s2" << " " << (s1==s2) << std::endl;
}

Ответы [ 2 ]

0 голосов
/ 28 октября 2018

Преобразование данных, имеющих более одного возможного представления, в «стандартную» или «нормальную» форму называется канонизацией.С текстом это обычно означает удаление акцентов, падежей, обрезку пробелов и / или символов форматирования.

Если канонизация выполняется во время каждого сравнения, то она хрупкая.Например, как вы проверяете, что это было сделано правильно для s1 и s2?Также он негибкий, например, вы не можете отобразить его результат или кэшировать его для следующего сравнения.Таким образом, это более надежный и эффективный способ сделать это в качестве явного шага канонизации.

В чем причина такого дополнительного «более короткого» порядка, если сравнение черты возвращает 0?

Сравнение признаков требуется для сравнения только n символов, поэтому при сравнении "hellow" и "hello" что должно возвращаться?Должен вернуть 0.Вы находитесь в дефектной ситуации, если вы как-то игнорируете это n, потому что черты должны работать с std::string_view, который не заканчивается нулем.Если сравнение размеров пропущено, то "hellow" и "hello" будут сравниваться равными, что вы, вероятно, не хотите.

0 голосов
/ 28 октября 2018

В чем причина такого дополнительного «более короткого» порядка, если сравнение черты возвращает 0?

Это просто, как basic_string::compare() определяется .

И есть ли обходной путь или я должен свернуть свою собственную строку?

Кажется, что ваш пользовательский char_traits должен реализовать:

  • length(), возвращая длину обрезанной части строки, и

  • move() и copy(), для копирования этой обрезанной части


Однако существует потенциальная проблема, которая не может быть решена с помощью пользовательских черт.У basic_string есть конструкторы, такие как basic_string(const CharT* s, size_type count, Allocator& alloc), или перегрузки методов, такие как assign или compare, которые принимают строку C и ее длину - в этих случаях Traits::length() вызываться не будет.Если кто-либо использует один из этих методов, строка может содержать конечные пробелы или пытаться получить доступ к символам за концом исходной строки.

Чтобы решить эту проблему, можно сделать что-то вроде этого:

class TrimmedString
{
public:
    // expose only "safe" methods:
    void assign(const char* s) { m_str.assign(s); }

private:
    std::basic_sttring<char, CustomTraits> m_str;
};

Или это (может быть проще):

class TrimmedString : private std::basic_string<char, CustomTraits>
{
public:
    using BaseClass = std::basic_string<char, CustomTraits>; // for readability

    // make "safe" method public
    using BaseClass::length;
    using BaseClass::size;
    // etc.

    // wrappers for methods with "unsafe" overloads
    void assign(const char* s) { BaseClass::assign(s); }
};
...