【问题标题】:Using strtok in C++在 C++ 中使用 strtok
【发布时间】:2012-12-06 15:25:14
【问题描述】:

我正在尝试读取具有以下输入(时间和价格)的文件:12:23:31 67 12:31:23 78 [...] 我创建了一个 struct,其中包含小时值, 分和秒。我使用strtok 标记各个值 并使用atof 来存储它们。但是,当我尝试时出现错误 标记时间:无法转换std::string' to 'char*' for argument 1 to 'char*'

struct time
{
    int hours;
    int minutes;
    int seconds;
    double price;
};

int main()
{
    string file, input;
    time* time_array;
    char* tok;

    cout << "Enter a file name to read input: ";
    cin >> file;

    ifstream file_name(file.c_str());

    file_name >> input;
    file_name >> input;

    //while(!file_name.eof())
    for(int i = 0; i < 4; i++)
    {
        time_array = new time;
        file_name >> input;
        tok = strtok(input, ":"); //ERROR HERE
        while(tok != NULL)
        {
            *time_array.hours = atof(tok[0]);
            *time_array.minutes = atof(tok[1]);
            *time_array.seconds = atof(tok[2]);
        }
        file_name >> input;
        *time_array.prine = atof(input);
    }
}

【问题讨论】:

  • 嗯,没有任何从stringchar * 的隐式转换。您将需要使用c_str() 方法。
  • @0A0D 还有?是的,你不能直接使用c_str(),你需要复制一个字符串。
  • 这是一种 XY 问题,您曾说过“我正在尝试使用 strtok 解析 C++ 中的字符串”,而您应该提出的问题是“我应该如何解析 C++ 中的字符串?” 那个问题的答案肯定不会是“使用strtok”。
  • @Let_Me_Be:是的,但是从您的评论来看,未来的读者并不清楚。

标签: c++ string io token


【解决方案1】:

我根本不会在这份工作中使用strtok1。如果你想使用类似 C 的工具,那么用 fscanf 读取数据:

// note there here `file_name` needs to be a FILE * instead of an ifstream.
fscanf(file_name, "%f:%f:%f %f", &hours, &minutes, &seconds, &price);

虽然大多数编写 C++ 的人更喜欢类型安全的东西。一种可能性是使用基本相同的格式字符串通过Boost.format 读取数据。

另一种可能性是使用流提取器:

char ignore1, ignore2;
file >> hours >> ignore1 >> minutes >> ignore2 >> seconds >> price;

至于它的作用/工作原理:每个提取器从输入流中读取一项。 float 的提取器每个读取一个数字。 char 的提取器每个读取一个字符。在这种情况下,我们希望看到:99:99:99 99,其中9 表示“一个数字”。因此,我们读取一个数字、一个冒号、一个数字、一个冒号、一个数字和另一个数字(提取器会自动跳过空格)。这两个冒号被读入char 变量,可以忽略不计,也可以检查它们是否真的是冒号,以验证输入数据的格式是否正确。

这是该技术的完整、可编译的演示:

#include <iostream>


int main() {
    float hours, minutes, seconds, price;
    char ignore1, ignore2;

    std::cin >> hours >> ignore1 >> minutes >> ignore2 >> seconds >> price;

    std::cout << "H:" << hours 
              << " M:" << minutes 
              << " S:" << seconds 
              << " P:" << price << "\n";
    return 0;
}

当然还有更多的可能性,但至少这些是一些合理的。


  1. 说实话,我不确定是否有任何工作我会使用strtok,但有些地方我可能至少有点想,或者希望@987654332 @ 的设计并没有那么糟糕,所以我可以使用它。然而,在这种情况下,我什至没有太多理由使用类似于 strtok 的任何东西。

【讨论】:

  • +1:这是另一种方式:while( getline(input, token, ' ') ) {...}
  • 或者把所有东西都塞进一个向量并使用迭代器。
  • 我查了一下,它是 istream 的成员,我已经包含了 iostream,但错误仍然存​​在
  • std::cin.ignore(1) 也许?还是&lt;tuple&gt;中的那个?
  • @Josh:抱歉,想一想,我不确定您是否可以将ignore 用作操纵器。我已经编辑将这些字符读入char 变量。这确实有一个小好处:如果你想验证输入,你可以检查那些是冒号。
【解决方案2】:

strtok 不采用 string 作为其参数 - 它采用 char*。与 cstring 标头中的所有函数一样,它是一个适用于 C 字符串(而不是 C++ 字符串)的 C 函数,通常不应在 C++ 中使用。

改用字符串类的方法。

【讨论】:

  • 它也不应该在 C 中使用。这是由于历史原因而存在的功能之一,但没有可接受的安全使用。
【解决方案3】:

简短的回答是您不能直接将std::stringstrtok 一起使用,因为strtok 需要一个可以修改的字符串。即使您使用c_str()std::string 中获取C 风格的字符串,它仍然是只读的。

如果你真的想使用strtok,你需要将字符串复制到一个可修改的缓冲区中,例如:

char* str = strdup(input.c_str());

如果这样做,请确保在函数末尾调用free(str),否则会出现内存泄漏!

【讨论】:

  • 但请注意 strdup 不可移植(并且尝试定义您自己的名为 strdup 的函数会产生未定义的行为)。
【解决方案4】:

您的简单案例可以使用string::find 方法轻松构建。但是,请查看Boost.Tokenizer

strtok 不能与 std::string.c_str() 一起使用,因为它返回 const char*strtok 不采用 string 作为参数,而是采用 char*

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-24
    • 2023-04-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多