标记字符串,识别字符时出错
Posted
技术标签:
【中文标题】标记字符串,识别字符时出错【英文标题】:Tokenizing a string, errors with recognizing char 【发布时间】:2012-02-12 17:57:57 【问题描述】:我在完成 C++ 的学校作业时遇到了一些麻烦。我遇到的具体问题涉及从包含一系列 5 到 6 级的文件中读取行。每个学生的成绩一起显示在学生姓名和身份证号后面的一行中。这里的挑战是等级之间可以有可变数量的空格,如果只有 5 个等级存在,则需要生成一条错误消息进行筛选,但程序要继续运行并对 5 个等级进行平均。输入示例:23 46 68 85 98
我很容易得到学生姓名和身份证,但数字字符串给我带来了问题。我的计划是 getline 然后标记字符串并将每个标记分配给数组中的一个单元格。这适用于 6 个等级,但当仅存在 5 个等级时,它会将垃圾分配给第六个单元格。
这是与本节相关的代码的 sn-p:
fin.getline(gradeList, 200);
grade = strtok (gradeList, " ");
while (grade != '\0')
gradeArr[cycler] = atoi(grade);
grade = strtok(NULL, " ");
cycler++;
我尝试在将每个标记转换为 int 之前对其进行 isdigit 检查,并为任何未通过 isdigit 检查的标记写入 0,但这根本不起作用。当只有 5 个等级时,它似乎正在从下一行中提取名称,然后当它出现时,它会将其更改为一个巨大的数字。
我认为当程序执行 getline 时,它只会抓取该行,直到它看到 endline 终止符。这不是正在发生的事情吗?
【问题讨论】:
【参考方案1】:摒弃 C 的废话,使用真正的 C++:
#include <string>
#include <sstream>
#include <fstream>
#include <vector>
// ...
std::vector<int> grades;
std::string line;
while (std::getline(fin, line))
std::istringstream iss(line);
int grade;
while (iss >> grade)
grades.push_back(grade);
这是一个更紧凑和优雅的方法,使用 istream-iterators 和 back-inserters:
#include <iterator>
#include <algorithm>
// other headers as before
std::vector<int> grades;
for (std::string line; std::getline(fin, line); )
std::istringstream iss(line);
std::copy(std::istream_iterator<int>(iss), std::istream_iterator<int>(),
std::back_inserter(grades));
关键是从流中提取格式化标记 (>>
) 已经完全符合您的要求,并且它包含标记化和解析为整数。
istream_iterator
封装了令牌提取,并允许您将流视为已解析令牌序列。 copy
算法然后简单地将这个序列复制到一个向量中,将它插入到容器的末尾。
【讨论】:
很遗憾,我们被禁止使用字符串库。 cstring 很好,只是没有字符串。【参考方案2】:如果您使用strtol
或strtod
,它会返回一个指向您刚刚处理的内容末尾的指针,因此您可以从那里继续。无需标记化:)
但听起来你的问题是你读错了行。在开始解析之前打印出gradeList
变量。
你应该得到这样的结果:
fin.getline(grade_text, 200);
const char* grade = grade_text;
const char* next_grade;
double grade_sum = 0;
for( int grade_count = 0; grades[grade_count] = strtol(grade, &next_grade, 10), next_grade > grade; ++grade_count )
grade_sum += grades[grade_count];
double mean_grade = grade_sum / grade_count;
【讨论】:
你还需要跳过空格吗? @KerrekSB:一切都为您处理。 “字符串(的初始部分)的预期形式是可选的前导空格,如 isspace(3) 所识别,可选的加号 ('+') 或减号 ('-'),然后是 (i) 小数数字,或 (ii) 十六进制数字,或 (iii) 无穷大,或 (iv) NAN(非数字)。” 谢谢,这很好。是的,这会比 iostreams 快得多,而且它是一个很好的选择,即使在 C++ 中也是如此。 我检查了它是否读取了错误的行,当我计算字符串时,它显示的数字是正确的,分数为 5 或 6。但是,当我对其进行标记时,它可以完美地使用 6 个等级,但是当我 coutgradeArr[5] 时,它会给我 5 个类似 1999398543 的东西。 @LuckyMeadows:嗯,你用cycler
计算你的有效成绩。所以不要使用任何无效的。顺便说一句,gradeArr[6]
是第 7 个值,无论如何都会成为垃圾。以上是关于标记字符串,识别字符时出错的主要内容,如果未能解决你的问题,请参考以下文章