【问题标题】:Pulling String and Integer Data From Text File从文本文件中提取字符串和整数数据
【发布时间】:2020-07-26 01:38:55
【问题描述】:

我完全是 C++ 初学者,但我在项目中遇到了一些问题。 我有一个文本文件,其中列出了员工姓名和他们工作的小时数。在文件中,名称和时间偶尔会重复,并记录每一天。有些名字比其他名字出现的频率更高。

例如:
约翰·史密斯 8
约翰·史密斯 7
约翰·史密斯 8
简·琼斯 9
简·琼斯 8
麦克斯韦 Ko 7
麦克斯韦 Ko 8
麦克斯韦 Ko 8
麦克斯韦 Ko 8
(数字代表工作时间)

到目前为止,我的代码看起来像这样,但我在分离数据时遇到了问题:

#include <iostream>
#include <string>
#include <fstream>

using namespace std;

int main()
{
    //Declare variables
    const double tax = 0.12;
    const double rate = 18.00;
    
    
    string firstName, lastName;
    int hours;
    
    
    ifstream inputFile;
    string filename = "/Users/luie/Desktop/employeehours.txt";
    
    
    cout << "Enter file name: ";
    cin >> filename;
    
    inputFile.open(filename);
    string name = firstName + " " + lastName;
    
    if(inputFile)
    {
        while(inputFile >> firstName >> lastName >> hours)
        {

            if(firstName == "John" && lastName == "Smith")
            {
                while(firstName == "John" && lastName == "Smith")
                {
                    int JohnHours = hours *= hours;
                    cout << "Total Hours Worked: " << JohnHours << endl;
                }
                if 
            }
            
            
            cout << "Employee Name: " << firstName << " " << lastName << " \n Hours Worked: " << hours << endl;
        }
       
            
       inputFile.close();
    }
        
    else
    {
        cout << "Error opening file. /n";
    }
    
    
    return 0;
}

我将如何提取字符串信息(员工姓名)并将其与整数信息(工作时间)分开?此外,我想将每个人的总工作时间相加,为每个人创建一个“总工作时间:”输出。我将不得不使用总工作时间来计算总和净工资计算器,其中工资为每小时 18 美元,税为 %12。感谢您的帮助!

【问题讨论】:

标签: c++


【解决方案1】:

一种 cstdio 方法

嗯,有很多方法可以将行拆分为 namehours 工作。简单的方法是使用C fscanf()(对于直接读取来说很脆弱)或将整行读入std:string,然后使用.c_str()sscanf()将行分隔成namehours(输入行格式的变化不会导致读取所有后续行失败)。使用fscanf()sscanf(),您可以使用格式字符串" %[^0-9]%lf" 将行解析为namehoursdouble 值(您需要从name 修剪尾随空格)。你可以这样做:

#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
#include <cstdio>

#define NAMSZ 128

int main (int argc, char **argv) {
    
    if (argc < 2) { /* validate 1 argument given for filename */
        std::cerr << "error: filename required as 1st argument.\n";
        return 1;
    }
    
    std::ifstream f (argv[1]);  /* open filename provided as 1st argument */
    
    if (!f.is_open()) { /* validate file is open for reading */
        std::cerr << "file open failed: " << argv[1] << '\n';
        return 1;
    }
    
    std::string tmp {};
    
    while (getline (f, tmp)) {      /* read each line into tmp */
        char name[NAMSZ] = "";      /* buffer to hold name */
        double hours = 0.;          /* double to hold hours */
        
        /* separate name & hours from line (protect array bound w/field-width) */
        if (sscanf(tmp.c_str(), " %127[^0-9]%lf", name, &hours) == 2)
            std::cout << std::left << std::setw(32) << name << hours << '\n';
    }
}

使用/输出示例

在文件dat/hoursworked.txt 中输入您的示例,您将收到以下信息:

$ ./bin/workerhours_cstdio dat/hoursworked.txt
John Smith                      8
John Smith                      7
John Smith                      8
Jane Jones                      9
Jane Jones                      8
Maxwell Ko                      7
Maxwell Ko                      8
Maxwell Ko                      8
Maxwell Ko                      8

但现在剩下的就是如何收集所有相似的名字,然后总结工作时间。

(注意: 混合使用 csdtio 函数和 iostream 是完全可以的,并且在许多情况下将提供比其他方法更好的性能。请参阅 C++ iostreams: Unexpected but legal multithreaded behaviour 中有关混合使用的其他说明)

映射每个姓名的总工作时间

当您考虑协调一组独特的项目时,您首先想到的应该是std::mapstd::unordered_map。两者都提供了一种基于唯一key 关联唯一对象集合的方法。 (在这种情况下你的name)。 unordered_map 不以任何排序顺序保存对象,而 map 根据您提供的排序功能(或默认值,例如 std::greater)对对象进行排序。

在简单的情况下,您可以在&lt;std::string, double&gt; 之间创建一个映射,然后使用.find() 成员函数来确定映射中是否已经存在name,如果存在,只需添加当前的小时数线到现有的映射,如果没有,添加新的namehours 对作为新元素。

要完成您的操作,您只需从名称中删除任何剩余的空格(上面未完成)并使用map(或unordered_map)创建唯一的name/hours 值对。只需简单的添加即可:

#include <map>
...
    std::map<std::string, double> workers {}; /* map of workers */
    ...
        /* separate name & hours from line (protect array bound w/field-width) */
        if (sscanf(tmp.c_str(), " %127[^0-9]%lf", name, &hours) == 2) {
            tmp = name;                         /* re-use tmp to make std::string */
            while (isspace(tmp.back()))         /* while trailing whitespace remains */
                tmp.pop_back();                 /* trim from end of sting */
            
            auto srch = workers.find(tmp);      /* search for name in map */
            if (srch != workers.end())          /* if name already in map */
                srch->second += hours;          /* add to hours */
            else    /* name not found */
                workers[tmp] = hours;           /* add new name to map */
        }

所以上面,在分离了初始的namehours 字符串之后,std::string tmp 被重新用于从name 创建一个std::string,然后isspace(tmp.back()) 用于检查是否最后一个字符是空格,如果是,则使用tmp.pop_back() 将其删除。

使用std::map workers,执行搜索以查看tmp 中的名称是否已存在,如果存在,则将小时数简单地添加到现有小时数(您使用workers-&gt;first 来引用@ 987654368@(名称)和workers-&gt;second 在使用srch 迭代器时引用映射值(hours)。如果名称不存在,则添加新映射。

总而言之,你会:

#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
#include <cstdio>
#include <cctype>
#include <map>

#define NAMSZ 128

int main (int argc, char **argv) {
    
    if (argc < 2) { /* validate 1 argument given for filename */
        std::cerr << "error: filename required as 1st argument.\n";
        return 1;
    }
    
    std::ifstream f (argv[1]);  /* open filename provided as 1st argument */
    
    if (!f.is_open()) { /* validate file is open for reading */
        std::cerr << "file open failed: " << argv[1] << '\n';
        return 1;
    }
    
    std::string tmp {};
    std::map<std::string, double> workers {}; /* map of workers */
    
    while (getline (f, tmp)) {      /* read each line into tmp */
        char name[NAMSZ] = "";      /* buffer to hold name */
        double hours = 0.;          /* double to hold hours */
        
        /* separate name & hours from line (protect array bound w/field-width) */
        if (sscanf(tmp.c_str(), " %127[^0-9]%lf", name, &hours) == 2) {
            tmp = name;                         /* re-use tmp to make std::string */
            while (isspace(tmp.back()))         /* while trailing whitespace remains */
                tmp.pop_back();                 /* trim from end of sting */
            
            auto srch = workers.find(tmp);      /* search for name in map */
            if (srch != workers.end())          /* if name already in map */
                srch->second += hours;          /* add to hours */
            else    /* name not found */
                workers[tmp] = hours;           /* add new name to map */
        }
    }

    for (const auto& w : workers)               /* output results */
            std::cout << std::left << std::setw(32) << w.first << w.second << '\n';
}

使用/输出示例

$ ./bin/workerhours_cstdio_map dat/hoursworked.txt
Jane Jones                      17
John Smith                      23
Maxwell Ko                      31

如果使用unordered_map,预计会出现类似以下的输出:

$ ./bin/workerhours_cstdio_umap dat/hoursworked.txt
Maxwell Ko                      31
John Smith                      23
Jane Jones                      17

这可能是分离namehours 并协调小时总和的更简单方法之一。

使用 std::string 成员函数分隔名称和小时

当然也可以使用std::string 成员函数,如.find_first_of().substr() 来处理分离。 (您的编译器至少需要支持-std=c++11)这种方法看起来类似于:

    std::string name;
    double hours;
    ...
        const char *digits = "0123456789";
        std::string line {};
        while (getline (f, line)) {                             /* read line */
            size_t hoursbegin = line.find_first_of(digits);     /* find 1st [0-9] */
            if (hoursbegin != std::string::npos) {              /* valdiate found */
                std::string tmp = line.substr(0, hoursbegin);   /* get name */
                while (isspace(tmp.back()))                     /* remove trailing */
                    tmp.pop_back();                             /* .. spaces */
                name = tmp;                                     /* assign to name */
                hours = stod(line.substr(hoursbegin));          /* assign to hours */
            }
        }

两者都一样好,都可以处理如下行:

John J. Doe, III, M.D.  10.8

而不是简单的名字、姓氏、小时线。

查看一下,如果您还有其他问题,请告诉我。

【讨论】:

  • 如果您要对正确答案投反对票,请诚实地发表评论。真的很难过,我们不能比这更好地对待彼此。
  • 谢谢。让我大吃一惊的是,有些人是多么肤浅——但话又说回来,这与一般社会没有什么不同......我们所能做的就是提供一个积极的例子。
猜你喜欢
  • 1970-01-01
  • 2023-04-09
  • 1970-01-01
  • 2016-06-13
  • 2012-11-22
  • 1970-01-01
  • 1970-01-01
  • 2012-02-01
  • 2013-07-03
相关资源
最近更新 更多