使用最多X个字符构造std::string,遇到空字符停止。

6

我正在从一个结构体中读取字符串,这些字符串在文件中有固定的长度,并使用'\0'进行填充。如果存储的字符串需要整个长度,则它们不是以零结尾的。

我目前正在使用以下方式构建std::string

// char MyString[1000];
std::string stdmystring(MyString, ARRAYSIZE(MyString));

然而,这也复制了填充。现在我可以修剪字符串,但有没有一种优雅且快速的方法来防止首次复制呢?
速度比空间更重要,因为这在循环中运行。

你可以使用POSIX的strnlen来确定大小。(你应该这样做,否则string::size()总是数组大小) - user2249683
没错,这绝对是一种优化。如果没有人想出如何在不使用第二个循环来确定大小的情况下完成此操作,我将使用此方法(这也是我所考虑的...)。 - Felix Dombek
3个回答

2

简单的解决方案有:

  1. 首先计算正确的长度

    • 可以使用 Dieter 建议的 strnlen
    • 或者使用 std::find(MyString,MyString+ARRAYSIZE(MyString),'\0'),这在我看来不会更慢

    请注意,如果您的字符串适合缓存,那么它很可能会支配额外的循环成本。

  2. 预留最大字符串大小(您说过空间不重要),并编写一个循环,将字符附加到超出宽度或达到 nul 时(如 copy_until

  3. 实际上创建一个初始化为 nuls 的最大大小字符串,strncpy 到其中,并选择擦除 unused nuls(如果您想要正确的大小)

第二个选项仅使用单个循环,而第三个则从概念上使用两个循环(在字符串构造函数中以及在复制中)。但是,每个字符的 push_back 似乎比简单的字符赋值更昂贵,因此我不会感到惊讶,如果 #3 在实际中更快。进行性能分析并查看!


至少在Visual Studio中,从已知大小构建字符串使用memcpy,速度比其他任何方法都要快,因此我会选择Dieter的方法。 - Felix Dombek

2

如果大小不是问题,一种可能的方法是创建一个空的std::string,然后使用reserve()预先分配可能需要的空间,然后添加每个字符,直到遇到'\0'

std::string stdmystring;
stdmystring.reserve(MyString_MAX_SIZE) ;
for(size_t i=0;i<MyString_MAX_SIZE && MyString[i]!='\0';++i);
stdmystring+=MyString[i];
< p > reserve() 函数可以保证你所需的内存分配,因为你知道字符串的最大大小并且它永远不会超过该大小。

对 += 运算符函数的调用可能会被内联,但仍然需要检查字符串是否具有所需的容量,这在您的情况下是浪费的。事实上,这可能与先使用 strlen 找到字符串的确切长度相同或更糟,因此您必须进行测试。


我稍后需要研究一些std::string的实现,但我认为在这里使用reserve可能是一种悲观的做法:它可能会阻止小字符串优化,在OP的情况下这可能很重要,具体取决于平均字符串长度。 - dyp
是的,这是正确的,但这取决于情况。短字符串必须适合用于大小、容量和指针的空间,以及长字符串及其相应大小和一些标志位之间的空间。通常,短字符串的容量只比3个机器字稍短。 如果平均字符串大小可以适应该保留(reserve())的空间,那么性能确实会变差,但如果有很多足够长的字符串没有首先保留空间,由于多次分配和复制,情况可能会更糟。 - Abstraction

0

我认为最直接的方法是通过多分配一个字节来扩大内部MyString数组,始终将最后一个字节置为空,然后使用std::string的C字符串构造函数。(请记住,由于文件I/O限制,大多数情况下C字符串构造函数使用的算法应该是可以接受的)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接