在 C++ 中使用 strtok 解析带有 date/time 的 CSV 文件时遇到问题
trouble using strtok to parse CSV file with date/time in c++
我有一个包含日期的 CSV 文件(我从 excel 转换而来),但我在使用 strtok 函数解析它们时遇到了问题。当我从 CSV 文件中删除带有日期的列时,它工作正常,但否则我会收到错误消息。我已经玩了几个小时,但错误不会消失。我将不胜感激 help/advice!我是一个编程初学者。这是我所拥有的(粗体部分是我为程序工作而取出的部分,CSV 文件中没有日期):
void parseFile (ifstream &myfile) {
string line;
int n = 1;
while ( myfile.good() ) {
//storing each line into char array
getline(myfile,line);
char data[100];
for (int g = 0; g <= line.size(); g++) {
data[g] = line[g];
}
//using strtok to read char array
char * tok;
tok = strtok (data,",");
while (tok != NULL) {
temp.patientID = atol(tok);
tok = strtok(NULL, ",");
temp.result = atof(tok);
tok = strtok(NULL, ",");
tok = strtok(NULL, "/");
tok = strtok(NULL, "/");
temp.date = atof(tok);
tok = strtok(NULL, " ");
tok = strtok(NULL, ":");
tok = strtok(NULL, ":");
temp.time = atof(tok);
tok = strtok(NULL, ",");
//getting each component and storing into map that stores parsed info, mymap
mymap.insert (pair<int,testInfo>(n,temp));
n++;
}
}
}
我的 CSV
文件看起来像这样:
1000856,0,28/09/2014 02:34:37
1002259,0.008,15/09/2014 23:14:11
1002259,0.002,18/09/2014 10:44:18
1002259,0.005,18/09/2014 16:54:52
1003348,0.038,20/03/2015 12:50:46
您正在搜索一个逗号然后需要,这意味着您在 temp.result = atof(tok); tok = strtok(NULL, ",");
之后的令牌指向字符串的末尾。删除那条线,我认为它应该做你想做的。当您尝试将 NULL 输出从 strtok 转换为浮点数时,会发生段错误。
查看这一行:
1000856,0,28/09/2014 02:34:37
我看到以下分隔符:,,// ::
你为 ,,,// ::,
努力
前面的 ', 太多了。
该行将解析为:“1000856”“0”“28/09/2014 02:34:37”NULL NULL NULL NULL NULL NULL,这些 NULL 将对 atofs
造成严重破坏。
此外,temp.date = atof(tok);
不会给你约会。它会给你部分日期。我认为的月份。 temp.time = atof(tok);
只是给你秒。
对于此任务,strtok 的更好选择是 stringstream 和 getline(stream &, string &, char)。这家伙让你设置分隔符等东西是这样的:
std::stringstream linestream(line)
std::string tok;
if (getline(linestream, tok, ','))
{ // got a token
try
{
temp.patientID = std::stol(tok);
}
catch (...)
{
// handle bad input number
}
}
我还将 atol
替换为 std::stol,因为有了它,您可以留在字符串区域并在数字未转换时捕获异常。 atol
无法转换时静默失败。
如果不允许使用流,请查看 sscanf。对于像您这样的定期格式化数据,与 strtok 相比,它是炸弹。
顺便说一句,你知道在将数据传递到 strtok 之前需要从 line 中复制数据,这对你很好。
上面的代码很容易写,但执行起来可能会很慢,但在这里应该没问题。
我上面的建议已经过时了,这里有一些要看的东西:
char data[100];
for (int g = 0; g <= line.size(); g++) {
data[g] = line[g];
}
g <= line.size()
将始终在字符串末尾读取一个。很棒,因为它保留了 NULL,但不完全符合犹太洁食标准。我不确定 C++ 是否保证那里会有 NULL。这些天我可能应该购买并阅读标准的副本。除了测试 g <= line.size()
之外,您还应该测试 g < 100
以防止溢出 char 数组。
在 while 循环内,如果 strtok returns NULL 因为格式不正确的行,则没有任何东西可以防止破坏。
如果mymap是std::map,你可以简化
mymap.insert (pair<int,testInfo>(n,temp));
到
mymap[n] = temp;
我有一个包含日期的 CSV 文件(我从 excel 转换而来),但我在使用 strtok 函数解析它们时遇到了问题。当我从 CSV 文件中删除带有日期的列时,它工作正常,但否则我会收到错误消息。我已经玩了几个小时,但错误不会消失。我将不胜感激 help/advice!我是一个编程初学者。这是我所拥有的(粗体部分是我为程序工作而取出的部分,CSV 文件中没有日期):
void parseFile (ifstream &myfile) {
string line;
int n = 1;
while ( myfile.good() ) {
//storing each line into char array
getline(myfile,line);
char data[100];
for (int g = 0; g <= line.size(); g++) {
data[g] = line[g];
}
//using strtok to read char array
char * tok;
tok = strtok (data,",");
while (tok != NULL) {
temp.patientID = atol(tok);
tok = strtok(NULL, ",");
temp.result = atof(tok);
tok = strtok(NULL, ",");
tok = strtok(NULL, "/");
tok = strtok(NULL, "/");
temp.date = atof(tok);
tok = strtok(NULL, " ");
tok = strtok(NULL, ":");
tok = strtok(NULL, ":");
temp.time = atof(tok);
tok = strtok(NULL, ",");
//getting each component and storing into map that stores parsed info, mymap
mymap.insert (pair<int,testInfo>(n,temp));
n++;
}
}
}
我的 CSV
文件看起来像这样:
1000856,0,28/09/2014 02:34:37
1002259,0.008,15/09/2014 23:14:11
1002259,0.002,18/09/2014 10:44:18
1002259,0.005,18/09/2014 16:54:52
1003348,0.038,20/03/2015 12:50:46
您正在搜索一个逗号然后需要,这意味着您在 temp.result = atof(tok); tok = strtok(NULL, ",");
之后的令牌指向字符串的末尾。删除那条线,我认为它应该做你想做的。当您尝试将 NULL 输出从 strtok 转换为浮点数时,会发生段错误。
查看这一行:
1000856,0,28/09/2014 02:34:37
我看到以下分隔符:,,// ::
你为 ,,,// ::,
前面的 ', 太多了。
该行将解析为:“1000856”“0”“28/09/2014 02:34:37”NULL NULL NULL NULL NULL NULL,这些 NULL 将对 atofs
造成严重破坏。
此外,temp.date = atof(tok);
不会给你约会。它会给你部分日期。我认为的月份。 temp.time = atof(tok);
只是给你秒。
对于此任务,strtok 的更好选择是 stringstream 和 getline(stream &, string &, char)。这家伙让你设置分隔符等东西是这样的:
std::stringstream linestream(line)
std::string tok;
if (getline(linestream, tok, ','))
{ // got a token
try
{
temp.patientID = std::stol(tok);
}
catch (...)
{
// handle bad input number
}
}
我还将 atol
替换为 std::stol,因为有了它,您可以留在字符串区域并在数字未转换时捕获异常。 atol
无法转换时静默失败。
如果不允许使用流,请查看 sscanf。对于像您这样的定期格式化数据,与 strtok 相比,它是炸弹。
顺便说一句,你知道在将数据传递到 strtok 之前需要从 line 中复制数据,这对你很好。
上面的代码很容易写,但执行起来可能会很慢,但在这里应该没问题。
我上面的建议已经过时了,这里有一些要看的东西:
char data[100];
for (int g = 0; g <= line.size(); g++) {
data[g] = line[g];
}
g <= line.size()
将始终在字符串末尾读取一个。很棒,因为它保留了 NULL,但不完全符合犹太洁食标准。我不确定 C++ 是否保证那里会有 NULL。这些天我可能应该购买并阅读标准的副本。除了测试 g <= line.size()
之外,您还应该测试 g < 100
以防止溢出 char 数组。
在 while 循环内,如果 strtok returns NULL 因为格式不正确的行,则没有任何东西可以防止破坏。
如果mymap是std::map,你可以简化
mymap.insert (pair<int,testInfo>(n,temp));
到
mymap[n] = temp;