【问题标题】:C++ How to convert string to char*C++ 如何将字符串转换为 char*
【发布时间】:2009-10-07 15:28:42
【问题描述】:

我需要将字符串转换为 char * 以在 strtok_s 中使用,但一直无法弄清楚。 c_str() 转换为不兼容的 const char *。

另外,如果有人可以向我解释为什么第二个 strtok_s 函数(在循环内)是必要的,那将是一个很大的帮助。为什么我需要显式推进令牌,而不是例如它所在的 while 循环,它连续、隐式地获取文件的每一行。

while( getline(myFile, line) ) { // Only one line anyway. . . is there a better way?
    char * con = line.c_str();
    token = strtok_s( con, "#", &next_token);
    while ((token != NULL))
    {
        printf( " %s\n", token );
        token = strtok_s( NULL, "#", &next_token);
    }
}

相关question

【问题讨论】:

  • 为什么在 C++ 字符串上使用 strtok()? C++ 为这类事情提供了更好的工具。
  • 因为我不知道。你有什么建议?
  • 参见stackoverflow.com/questions/53849/… 使用 Boost 的示例代码。
  • 但是为什么这样更好呢?图书馆不会产生开销吗?
  • 顺便说一下,C++ 字符串中间可以有 NUL 字符,因为 C++ 是根据一些字节和长度来定义字符串的,而不是 C 的“以 NUL 结尾的字节序列”。因此,如果您只知道输入是一个 C++ 字符串,那么像 strtok 这样的 C 函数实际上不起作用,因为它们可能会在实际结束之前错误地检测到他们认为是字符串的结尾。

标签: c++ casting strtok


【解决方案1】:

使用strdup()c_str() 返回的const char * 复制到char * 中(之后记得free()

请注意,strdup()free() 是 C 而不是 C++ 函数,最好使用 std::string 的方法。

第二个 strtok_s() 是必需的,否则你的循环不会终止(token 的值不会改变)。

【讨论】:

  • 但是为什么我需要显式地推进令牌而不是例如它所在的 while 循环,它连续地、隐式地获取文件的每一行?
  • 再看代码: strtok() 的第一次调用从文件中获取该行的第一个标记。然后 while() 的条件检查 token 是否为 NULL。如果不是,则执行 printf(),并提取下一个标记。让您感到困惑的可能是变量 next_token 实际上并不存储下一个标记,而是存储该行的其余部分。这就是 strtok_s() 的工作方式。
  • strdup 不是标准 C。它只是一个常见的扩展,但绝不保证存在。
【解决方案2】:

您不能转换为char *,因为那将允许您写入std::string 的内部缓冲区。为避免使 std::string 的实现可见,这是不允许的。

代替strtok,尝试一种更“类似C++”的方式来标记字符串。看到这个问题:

How do I tokenize a string in C++?

【讨论】:

  • 似乎很难相信这个演员阵容是不可能的。
  • 演员本身可以使用 const_cast,但完全不建议。
  • 不可能的。原因是在面向对象编程中,对象不喜欢外部客户直接访问它们的内部表示。见en.wikipedia.org/wiki/Information_hiding
  • 你只需要相信它。从 const 到非 const 指针的转换是可能的,但尝试修改指向的数据具有未定义的行为。 c_str() 不需要返回字符串的内部缓冲区 - 它可以将字符串复制到新位置并显示给您。显然,修改原始字符串的任何此类克隆都行不通。在 C++0x 中,字符串的实现受到更严格的控制,IIRC 您将能够使用&line[0] 作为指向字符串数据的char*。虽然这可能不是 NUL 终止的。
【解决方案3】:

正如丹尼尔所说,你可以选择

strdup(line.c_str());

哪个比我最初提出的 strcpy 更好,因为它分配了必要的空间

【讨论】:

  • strcpy 给出折旧警告?
  • strdup(line.c_str()) 可能会更好。
  • 请注意:这不会开箱即用。你必须为con分配内存。
  • 它可能会在 VC++ 中给出弃用警告。出于安全原因(这使他们的操作系统看起来很糟糕),它已被 Microsoft 弃用,ISO 标准库中并未弃用它。警告消息还告诉您如何修复它(它建议了两种方法)。
【解决方案4】:

strtok() 一开始就是一个设计糟糕的函数。检查您的文档,看看您是否有更好的文档。顺便说一句,除非您的文档明确说明它是安全的,否则切勿在任何类型的线程环境中使用 strtok(),因为它会在调用之间存储状态并修改调用它的字符串。我认为strtok_s() 是一个更安全的版本,但它不会是一个真正安全的版本。

要将std::string 转换为char *,您可以:

char * temp_line = new char[line.size() + 1];  // +1 char for '\0' terminator
strcpy(temp_line, line.c_str());

并使用temp_line。你的安装可能有一个strdup() 函数,它会重复上面的。

您需要两次调用strtok_s() 的原因是它们执行不同的操作。第一个告诉strtok_s() 它需要处理什么字符串,第二个继续使用相同的字符串。这就是 NULL 参数的原因;它告诉strtok_s() 继续使用原始字符串。

因此,您需要一次调用来获取第一个令牌,然后为每个后续令牌调用一次。它们可以与类似的东西结合起来

char * temp_string_pointer = temp_line;
while ((token = strtok_s( con, "#", &next_token)) != NULL)
{
   temp_string_pointer = NULL;

等等,因为这将使用字符串指针调用一次strtok_s(),然后使用NULL。不要为此使用 temp_line,因为您想在处理后 delete[] temp_line;

您可能认为这很麻烦,但这就是strtok() 和亲戚通常需要的。

【讨论】:

  • 我会赞成你说“strtok() 是一个设计糟糕的函数”,但是你建议使用裸字符缓冲区而不是一些资源管理对象。 :(
【解决方案5】:

strtok 是这样工作的:

如果没有找到分隔符,则第一次调用返回字符串从开始的分隔符或所有字符串:

token = strtok_s(con, "#", &next_token);

使用 NULL 的第二次调用允许您继续解析相同的字符串以找到下一个分隔符:

token = strtok_s(NULL, "#", &next_token);

如果到达字符串末尾,下一次调用将返回 NULL;

【讨论】:

  • 但是为什么我需要显式推进令牌而不是例如它所在的 while 循环,它隐式地连续获取文件的每一行。
  • 你到底在问什么?您必须重复调用 strtok,直到您使用了您提供的数据中的所有令牌,在这种情况下是文件的一行。 while 循环检查 strtok 的结果以确保发生这种情况。
【解决方案6】:

只要你有一个std::string 并且你需要的是一个(可修改的)字符数组,那么std::vector<char> 就是你所需要的:

void f(char* buffer, std::size_t buffer_size);

void g(std::string& str)
{
  std::vector<char> buffer(str.begin(),str.end());
  // buffer.push_back('\0');    // use this if you need a zero-terminated string
  f(&buffer[0], buffer.size()); // if you added zero-termination, consider it for the size
  str.assign(buffer.begin(), buffer.end());
}

【讨论】:

    【解决方案7】:

    第二个 strtok 调用在循环内。它使您的令牌指针前进,以便您一个接一个地打印出令牌,直到您打印出所有令牌,指针变为空并退出循环。

    要回答问题的第一部分,正如其他人所建议的那样,c_str() 只为您提供内部缓冲区指针 - 您无法修改它,这就是它的原因。如果你想修改它,你需要分配自己的缓冲区并将字符串的内容复制到其中。

    【讨论】:

      【解决方案8】:

      如果你真的需要访问字符串的内部缓冲区,方法是:&amp;*string.begin()。 直接访问字符串的缓冲区在某些情况下很有用,here你可以看到这样的情况。

      【讨论】:

      • 我不想那样做。摆弄数据结构的内部通常是危险的。
      • 理论上,在这种情况下,因为std::string 甚至不保证它将其字符存储在连续的内存中(例如,std::vector 从 C+ +03)。实际上,没有人见过连续存储其字符的类的实现。正如 onebyone 所说,在 C++1x 中,这将得到保证。
      • 我上面的这些 cmets 是否得出结论认为这种方法可以安全使用?它似乎很容易解决我的问题..但我不想设置失败...
      【解决方案9】:

      您可以轻松编写一个转换例程,将一个字符串标记化并返回一个子字符串向量:

      std::vector<std::string> parse(const std::string& str, const char delimiter)
      {
          std::vector<std::string> r;
      
          if(str.empty())
              return r;
      
          size_t prev = 0, curr = 0;
      
          do
          {
              if(std::string::npos == (curr = str.find(delimiter, prev)))
                  curr = str.length();
      
              r.push_back(str.substr(prev, curr - prev));
              prev = curr + 1;
          }
          while(prev < (int)str.length());
          return r;
      }
      

      【讨论】:

        【解决方案10】:

        我认为您可以先将字符串转换为 const char*,然后将 const char* 复制到 char* 缓冲区以供进一步使用。

        【讨论】:

          猜你喜欢
          • 2018-03-27
          • 2013-05-13
          • 2012-01-16
          • 2016-02-02
          • 2018-09-02
          • 2016-04-20
          • 2012-03-07
          • 1970-01-01
          • 2010-11-22
          相关资源
          最近更新 更多