gcount() 的输出不一致
Inconsistent output from gcount()
我编写了以下简单的 MRE,它在我的程序中重新生成了一个错误:
#include <iostream>
#include <utility>
#include <sstream>
#include <string_view>
#include <array>
#include <vector>
#include <iterator>
// this function is working fine only if string_view contains all the user provided chars and nothing extra like null bytes
std::pair< bool, std::vector< std::string > > tokenize( const std::string_view inputStr, const std::size_t expectedTokenCount )
{
// unnecessary implementation details
std::stringstream ss;
ss << inputStr.data( ); // works for null-terminated strings, but not for the non-null terminated strings
// unnecessary implementation details
}
int main( )
{
constexpr std::size_t REQUIRED_TOKENS_COUNT { 3 };
std::array<char, 50> input_buffer { };
std::cin.getline( input_buffer.data( ), input_buffer.size( ) ); // user can enter at max 50 characters
const auto [ hasExpectedTokenCount, foundTokens ] { tokenize( { input_buffer.data( ), input_buffer.size( ) }, REQUIRED_TOKENS_COUNT ) };
for ( const auto& token : foundTokens ) // print the tokens
{
std::cout << '\'' << token << "' ";
}
std::cout << '\n';
}
这是一个标记化程序(有关完整代码,请参阅下面 link 处的 Compiler Explorer)。另外,我使用 GCC v11.2.
首先,我想避免使用 data()
,因为它的效率有点低。
我查看了 Compiler Explorer 中的程序集,显然,data()
调用了 strlen()
,所以当它到达第一个空字节时它停止了。但是如果 string_view
对象不是空终止的呢?这有点令人担忧。所以我切换到 ss << inputStr;
.
其次,当我执行此操作时 ss << inputStr;
,整个 50 个字符的缓冲区及其所有空字节都被插入到 ss
中。以下是一些错误的示例输出:
示例 #1:
1 2 3
'1' '2' '3 ' // '1' and '2' are correct, '3' has lots of null bytes
示例 #2(在这个示例中,我在 3 之后输入了一个 space 字符):
1 2 3
'1' '2' '3' ' ' // an extra token consisting of 1 space char and lots of null bytes has been created!
有办法解决这个问题吗?我现在应该怎么做才能同时支持 non-null terminated 字符串?我想出了如下 gcount()
的想法:
const std::streamsize charCount { std::cin.gcount( ) };
// here I pass charCount instead of the size of buffer
const auto [ hasExpectedTokenCount, foundTokens ] { tokenize( { input_buffer.data( ), charCount },
REQUIRED_TOKENS_COUNT ) };
但问题是,当用户输入的字符少于缓冲区大小时,gcount()
returns一个比实际输入的char
个数多1的值(例如,用户输入了 5 个字符,但 gcount
returns 6 显然也考虑到了 '\0'。
这导致最后一个标记的末尾也有一个空字节:
1 2 3
'1' '2' '3 ' // see the null byte in '3 ', it's NOT a space char
我该如何解决 gcount
的不一致输出?
或者也许我应该更改函数 tokenize
以便它摆脱 string_view
末尾的任何 '\0' 然后开始对其进行标记化。
虽然听起来像是 XY 问题。但我真的需要帮助来决定要做什么。
您遇到的基本问题是 operator<<
函数。您已经尝试过其中两个:
operator<<(ostream &, const char *)
将从指针获取字符直到(不包括)下一个 NUL。正如您所指出的,如果指针来自 string_view 而没有终止 NUL,则可能会出现问题。
operator<<(ostream &, const string_view &)
将获取 string_view 中的所有字符,包括可能存在的任何 NUL。
您似乎想要做的是从 string_view 到(不包括)第一个 NUL 或 string_view 的末尾提取字符,以先到者为准。您可以使用 find
并构造一个直到 NUL 或结尾的子字符串:
ss << inputStr.substr(0, inputStr.find('[=10=]'));
我编写了以下简单的 MRE,它在我的程序中重新生成了一个错误:
#include <iostream>
#include <utility>
#include <sstream>
#include <string_view>
#include <array>
#include <vector>
#include <iterator>
// this function is working fine only if string_view contains all the user provided chars and nothing extra like null bytes
std::pair< bool, std::vector< std::string > > tokenize( const std::string_view inputStr, const std::size_t expectedTokenCount )
{
// unnecessary implementation details
std::stringstream ss;
ss << inputStr.data( ); // works for null-terminated strings, but not for the non-null terminated strings
// unnecessary implementation details
}
int main( )
{
constexpr std::size_t REQUIRED_TOKENS_COUNT { 3 };
std::array<char, 50> input_buffer { };
std::cin.getline( input_buffer.data( ), input_buffer.size( ) ); // user can enter at max 50 characters
const auto [ hasExpectedTokenCount, foundTokens ] { tokenize( { input_buffer.data( ), input_buffer.size( ) }, REQUIRED_TOKENS_COUNT ) };
for ( const auto& token : foundTokens ) // print the tokens
{
std::cout << '\'' << token << "' ";
}
std::cout << '\n';
}
这是一个标记化程序(有关完整代码,请参阅下面 link 处的 Compiler Explorer)。另外,我使用 GCC v11.2.
首先,我想避免使用 data()
,因为它的效率有点低。
我查看了 Compiler Explorer 中的程序集,显然,data()
调用了 strlen()
,所以当它到达第一个空字节时它停止了。但是如果 string_view
对象不是空终止的呢?这有点令人担忧。所以我切换到 ss << inputStr;
.
其次,当我执行此操作时 ss << inputStr;
,整个 50 个字符的缓冲区及其所有空字节都被插入到 ss
中。以下是一些错误的示例输出:
示例 #1:
1 2 3
'1' '2' '3 ' // '1' and '2' are correct, '3' has lots of null bytes
示例 #2(在这个示例中,我在 3 之后输入了一个 space 字符):
1 2 3
'1' '2' '3' ' ' // an extra token consisting of 1 space char and lots of null bytes has been created!
有办法解决这个问题吗?我现在应该怎么做才能同时支持 non-null terminated 字符串?我想出了如下 gcount()
的想法:
const std::streamsize charCount { std::cin.gcount( ) };
// here I pass charCount instead of the size of buffer
const auto [ hasExpectedTokenCount, foundTokens ] { tokenize( { input_buffer.data( ), charCount },
REQUIRED_TOKENS_COUNT ) };
但问题是,当用户输入的字符少于缓冲区大小时,gcount()
returns一个比实际输入的char
个数多1的值(例如,用户输入了 5 个字符,但 gcount
returns 6 显然也考虑到了 '\0'。
这导致最后一个标记的末尾也有一个空字节:
1 2 3
'1' '2' '3 ' // see the null byte in '3 ', it's NOT a space char
我该如何解决 gcount
的不一致输出?
或者也许我应该更改函数 tokenize
以便它摆脱 string_view
末尾的任何 '\0' 然后开始对其进行标记化。
虽然听起来像是 XY 问题。但我真的需要帮助来决定要做什么。
您遇到的基本问题是 operator<<
函数。您已经尝试过其中两个:
operator<<(ostream &, const char *)
将从指针获取字符直到(不包括)下一个 NUL。正如您所指出的,如果指针来自 string_view 而没有终止 NUL,则可能会出现问题。operator<<(ostream &, const string_view &)
将获取 string_view 中的所有字符,包括可能存在的任何 NUL。
您似乎想要做的是从 string_view 到(不包括)第一个 NUL 或 string_view 的末尾提取字符,以先到者为准。您可以使用 find
并构造一个直到 NUL 或结尾的子字符串:
ss << inputStr.substr(0, inputStr.find('[=10=]'));