basic_string的前导/尾部不区分空格的特征

Leading/trailing whitespace insensitive traits for basic_string

本文关键字:不区 空格 特征 尾部 string basic      更新时间:2023-10-16

我正在进行大量的解析/处理,其中给出了前导/尾随空格和不区分大小写。因此,我为std::basic_string制作了一个基本的字符特性(见下文(,以节省一些工作。

特性不起作用,问题是basic_string的compare调用特性compare,如果求值为0,则返回大小差异。在basic_string.h中,它说

。。。如果比较结果为非零,则返回该结果,否则将先排序较短的结果如果trait的compare返回0,那么有这个额外的"shorter one">排序的原因是什么?还有,有什么变通办法吗?还是我必须自己动手?

#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;
}

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

如果在每次比较过程中都是在幕后进行规范化,那么它是脆弱的。例如,您如何测试对s1s2的操作是否正确?此外,它是不灵活的,例如,您不能显示它的结果或缓存它以备下次比较。因此,作为显式规范化步骤,这样做既更健壮又更高效。

如果trait的compare返回0,那么有这个额外的"较短的一个"排序的原因是什么?

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

如果trait的compare返回0,那么有这个额外的"较短的一个"排序的原因是什么?

basic_string::compare()就是这样定义的。

还有什么变通方法吗?或者我必须滚动自己的字符串吗?

您的自定义char_traits似乎必须实现:

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

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


然而,有一个潜在的问题无法使用自定义特性来解决。basic_string有像basic_string(const CharT* s, size_type count, Allocator& 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); }
};