【问题标题】:Leading/trailing whitespace insensitive traits for basic_stringbasic_string 的前导/尾随空格不敏感特征
【发布时间】:2023-03-30 05:38:01
【问题描述】:

我正在做很多解析/处理,其中给出了前导/尾随空格和不区分大小写。所以我为std::basic_string(见下文)制作了一个基本的字符特征来为自己节省一些工作。

特征不起作用,问题是basic_string 的比较调用特征比较,如果评估为 0,则返回大小差异。在basic_string.h 它说 ...如果比较的结果是非零返回它,否则首先排序较短的。 看起来他们明确不希望我这样做...

如果 trait 的比较返回 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;
}

【问题讨论】:

标签: c++ stdstring char-traits


【解决方案1】:

将具有多种可能表示形式的数据转换为“标准”或“正常”形式称为规范化。对于文本,它通常意味着删除重音、大小写、修剪空白字符和/或格式字符。

如果在每次比较期间都在后台进行规范化,那么它是脆弱的。例如,您如何测试s1s2 是否正确完成?它也很不灵活,例如你不能显示它的结果或缓存它以供下次比较。 因此,将其作为显式规范化步骤更稳健、更高效。

如果 trait 的比较返回 0,为什么会有这个额外的“更短”排序?

特征比较只需要比较n 字符,所以当你比较"hellow""hello" 应该返回什么?它应该返回0。如果您以某种方式忽略 n,您就处于有缺陷的情况,因为这些特征应该与非零终止的 std::string_view 一起使用。如果大小比较被删除,那么 "hellow""hello" 将比较等于您可能不想要的。

【讨论】:

  • 谢谢!!!花了我一段时间重新思考这个问题......我的特质唯一可行的方法是,如果我可以在具有修剪长度的特质中做这个“更短的”。但它肯定不可行,因为我不能覆盖basic_string 的比较方法。另一方面,触摸 trait 的 lenght() 简直是一团糟。我坚持显式修剪。再次感谢!!!
【解决方案2】:

如果 trait 的比较返回 0,为什么会有这个额外的“更短”排序?

basic_string::compare() 就是 defined

还有,有什么解决方法还是我必须自己滚动字符串?

看来您的自定义char_traits 必须实现:

  • length(),返回字符串修剪部分的长度,以及

  • move()copy(),用于复制修剪后的部分


但是,使用自定义特征无法解决一个潜在问题。 basic_string 有像 basic_string(const CharT* s, size_type count, Allocator&amp; alloc) 这样的构造函数,或者像 assigncompare 这样的方法重载,它们采用 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); }
};

【讨论】:

  • 感谢您的回答。如果length() 返回修剪后的大小,我将如何处理前导空格?例如。 " hello " -> [按照你的方法] -> " hell"。我看不到通过特征增加 basic_string 数据指针的方法:/。即使我会通过length() 以某种方式完成它,我想我会像basic_string 的追加等那样搞砸吗?
  • 我有点挣扎...如果我改变 length(),copy(),move() 等内部特征我会有很多“不安全”basic_string 方法@ 987654346@,insert,append...我基本上最终写了std::basic_string类。
  • begin() 是安全的,这里的问题是采用 C 字符串和长度的方法,例如 assign 的重载等。我的示例中的想法是包装 std::basic_string 以便它们不是上市。这样,就没有必要实现它们了——你只需要一个从basic_string调用方法的行包装器。
猜你喜欢
  • 2019-12-16
  • 1970-01-01
  • 2014-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-25
  • 2012-02-28
  • 2017-03-16
相关资源
最近更新 更多