标记字符串,识别字符时出错

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));

关键是从流中提取格式化标记 (&gt;&gt;) 已经完全符合您的要求,并且它包含标记化和解析为整数。

istream_iterator 封装了令牌提取,并允许您将流视为已解析令牌序列。 copy 算法然后简单地将这个序列复制到一个向量中,将它插入到容器的末尾。

【讨论】:

很遗憾,我们被禁止使用字符串库。 cstring 很好,只是没有字符串。【参考方案2】:

如果您使用strtolstrtod,它会返回一个指向您刚刚处理的内容末尾的指针,因此您可以从那里继续。无需标记化:)

但听起来你的问题是你读错了行。在开始解析之前打印出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 个值,无论如何都会成为垃圾。

以上是关于标记字符串,识别字符时出错的主要内容,如果未能解决你的问题,请参考以下文章

C# - 使用标记来识别值的子字符串总和

识别和标记重复字符

识别屏幕截图中字符的最佳方法?

字符型图片验证码识别完整过程及Python实现

编程思想亮晶晶

识别文本中的空格