【问题标题】:NUL char in strings in C++C++ 中字符串中的 NUL 字符
【发布时间】:2014-08-18 15:22:41
【问题描述】:

在下面的代码中,我试图用'\0' 替换第一个字符。

我希望它打印一个空字符串,但在输出中它只是省略了它并显示其余字符。

int main()
{
    string str;
    cin >> str;

    str[0] = '\0';

    cout << str << "\n";
    return 0;
}

输出:

   testing
   esting

如何在 C++ 中终止字符串?

PS:我正在尝试这种在另一个问题中终止字符串的方法,我需要在其中终止。

【问题讨论】:

  • NULL 是 #define 用于语言 C 中的 Null 指针。它有时也用作C++ 中的指针值,但这不是共识。但 NULL 绝不是字符常量。
  • 如果您尝试str.c_str(),它可能会给出您想要的输出,但我不建议以这种方式使用字符串。
  • 在尝试操纵std::string 的内部时要非常小心。内存通常以连续的方式布局(IIRC 因为std::string.c_str() 的要求,但您不应过多假设容器(std::string)如何操作和使用该内存。不建议尝试在容器不知情的情况下操作原始内容。在必要时使用std::string() 方法,有很多方法,在需要时使用标准算法;它们可以很好地协同工作。
  • @Niall 您通常无法操纵std::string 的内部结构,但是您可以像他所做的那样通过对字符串进行索引来修改特定字符,或者通过取消引用有效的迭代器,然后也是许多允许显式操作的非常量成员函数。
  • @Niall:C++11 要求 std::string 是连续的。但即使在 C++03 中,完全可以接受 OP 正在做的事情。您可以修改字符串中的任何字符(即任何0 ≤ i &lt; str.size()str[i] = anyChar),而根本不会“破坏”字符串。 FWIW,operator[] std::string 中故意添加的一个方法,以便人们可以做这样的事情。

标签: c++ string c++11


【解决方案1】:

std::string 不是以空字符结尾的字符串。如果要清空它,请使用 clear() 方法。如果要删除字符串的元素,请使用erase() 方法。

【讨论】:

  • @Niall 事实上,保证不会被实现为以空字符结尾的字符数组,因为 std::string 包含 '\0' 是完全合法的。
  • @JamesKanze 正确,我们不是说同样的话吗?
  • @Niall 您的“不保证”声明允许 std::string 实现为空终止,只是不能保证。 James 的“保证不”声明只允许非空终止的实现,这是正确的。
  • @Niall 你说“不保证”,我说其实是禁止的。
  • @Niall 可以将'\0' 放在字符串中的事实意味着实现不能将其用作哨兵。 (C++11 确实要求内存是连续的,并且 c_str() 返回与 &amp;str[0] 相同的内容,但这仍然不允许实现将 '\0' 视为终止符。)
【解决方案2】:

字符串有两种方法。

在 C 中,字符串以零结尾,这意味着 '\0' 表示字符串的结尾,这是您所期望的。

C++(和大多数语言)使用计数字符串,这意味着字符串的结尾被索引,因此向字符串添加空终止符不会终止它。 '\0' 是一个非打印字符,所以当你打印时,你会得到你所看到的行为。如果要操作 std::string 长度,则需要使用 std::string 方法(http://www.cplusplus.com/reference/string/string/)。

C++ 不关心字符串的空终止符。它们仅用于 C 兼容性。

顺便说一句,这应该具有您所期望的行为。

cout<<str.c_str()<<"\n";

另见

Why null-terminated strings? Or: null-terminated vs. characters + length storage

What's the rationale for null terminated strings?

【讨论】:

  • +1,尽管“C++ 使用长度前缀字符串”——不是字面意义上的“前缀”(比如 Borland Pascal)——如果有一个长度数据成员,它几乎肯定会是一个单独的数据成员从指向动态分配字符的指针开始,并且不覆盖其前几个字节,尽管通过短字符串优化,它有时可能会巧合地在内存布局方面成为前缀。其他字符串实现可以存储指向数据末尾的指针而不是大小。
  • 我真的很想知道如何描述 C++ 的字符串存储风格,然后又回到了我曾经读过的关于 pascal 的内容。对于索引最后一个字符的一般做法,有什么更好的术语?
  • 我一直都知道它们是“计数字符串”。
  • 不知道如何简洁地说:-/。我倾向于说“std::string 有一个 size 成员,与文本数据分开,所以它不需要哨兵来标记结束。”
【解决方案3】:

你只是认为你得到了

testing
esting

作为输出,但你实际上得到了

testing
 esting
^
|
+-- "empty" \0 char

因为std::string 的长度仍为“testing”,所以您只需将第一个字符“t”替换为“\0”。当std::cout 获取字符串时,它会查看字符串 length 并输出其所有字符,这使得 '\0' 导致输出中出现“空”槽。

要真正清除字符串,最好调用std::string::clear()std::string::reset(0) 也是有效的,但它的表现力不强(你甚至可以分配一个空字符串......不寒而栗)。该实现可能会或可能根本不使用“\0”,因此不要将其视为摆弄外部观察到的表示的一种方式。

【讨论】:

    【解决方案4】:

    std::stringstd::vector 的功能非常相似。如果你想删除 std::string 的所有元素,你可以使用 std::string::clear

    #include <iostream>
    #include <string>
    
    int main() {
      std::string str("testing");
      std::cout << str << std::endl;
      str.clear();
      std::cout << str << std::endl;
    }
    

    如果您想从字符串中删除特定字符(例如,第一个字符),您可以使用 std::string::erase:

    #include <iostream>
    #include <string>
    
    int main() {
      std::string str("testing");
      std::cout << str << std::endl;
      str.erase(str.begin());
      std::cout << str << std::endl;
    }
    

    如果您想从字符串中删除特定字符,例如 std::vector,请使用擦除删除习语:

    #include <iostream>
    #include <string>
    #include <algorithm>
    
    int main() {
      std::string str("testing");
      std::cout << str << std::endl;
      str.erase(std::remove_if(str.begin(), str.end(), [](char const &c){ return c == 't'; }), str.end());
      std::cout << str << std::endl;
    }
    

    【讨论】:

      【解决方案5】:

      根据 std::string 实现,[] 返回内部 char 数组中给定位置的字符的引用。当您设置为 str[8]='\0' 时,它会设置。您可以通过调用 str.c_str() 函数来检查它。它返回内部数组。

      然而, cout 读取它的字符,中间没有空字符。这就是输出的原因。

      const_reference operator[](size_type __n) const
      {
          return *(_M_start + __n);
      }
      
      reference operator[](size_type __n)
      {
          return *(_M_start + __n);
      }
      

      【讨论】:

        【解决方案6】:

        要获取以第一个字符结尾的字符串,您需要首先通过调用方法string::c_str()string 中提取char const*。然后它将按照您的意愿进行处理(就像在 C 中一样)。

        【讨论】:

          【解决方案7】:

          也许使用像printf("%s", str.c_str()); 这样的旧 printf 这将打印出 C 风格,并且应该以空字符终止。 strlen(str.c_str()); 之类的东西也应该可以工作。我怀疑的是,由于 C++ 字符串在某处有一个 .size() 函数,它可能有一个 int size; 成员,因此他们不会在每次想要打印时浪费任何时间检查空字符。他们可能只是说打印 str.size() 个字符。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2018-07-27
            • 1970-01-01
            • 2018-09-03
            • 2013-02-04
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-04-11
            相关资源
            最近更新 更多