如何搜索文件中的数字?

时间:2015-05-23 13:23:03

标签: c++

在每一行中,都有一个字符串和一个数字。我已经完成搜索功能以搜索名称并且它可以正常工作

例如,在包含以下内容的文件中:

Batman 290
Joker 100
Spiderman 300

但我现在面临的问题是如何修改字符串旁边的数字。例如,当我搜索“蝙蝠侠”这个名字时,我只想改变蝙蝠侠的号码。

以下是添加代码:

void Teacher::addScore(string name, double score) {
    outStream.open("StudentRecord.txt", ios_base::app);
    outStream << name << " || " << score << endl;
    outStream.close();
}

搜索

string Teacher::search(string searchKeyword) {
    int count = 0;
    string name;
    readStream.open("StudentRecord.txt");

    while (readStream.is_open()) {
        getline(cin, name);

        while (readStream >> name) {
            if(name == searchKeyword) {
                count++;
                return name;

            }
        }
        if(name != searchKeyword) {
            return "That student doesnt exist";
        }
    }
    return "";
}

为了修改,我在这个阶段遇到了麻烦

void Teacher::modifyScore() {
    string name;
    string searchResult;
    cout << "You want to modify the a student score?" << endl;
    cout << "Which student's you want to change?" << endl;
    cin >> name;
    searchResult = search(name);
    outStream.open("StudentRecord.txt", ios::trunc);
    // What should I do here to modify a student number?  
}

3 个答案:

答案 0 :(得分:2)

第二次编辑:使用读/写代码

请注意,代码不是最佳的,并且没有任何错误检查。假设数据位于“data.txt”中。

#include <fstream>
#include <iostream>
#include <string>
#include <sstream>
#include <map>

int main()
{
    // Holds file data
    std::map<std::string, int> data;

    // Read file and fill data map
    std::ifstream ifs("data.txt");
    std::string line;
    while (std::getline(ifs, line))
    {
        std::string name;
        int number;
        std::stringstream ss(line);
        ss >> name >> number;
        data[name] = number;
    }
    ifs.close();

    // Print data
    for (auto& entry : data)
    {
        std::cout << entry.first << " " << entry.second << std::endl;
    }
    // Modify data
    data["Batman"] += 100;

    // Open same file for output, overwrite existing data
    std::ofstream ofs("data.txt");
    for (auto& entry : data)
    {
        ofs << entry.first << " " << entry.second << std::endl;
    }
    ofs.close();

    return 0;
}

编辑:原始答案假定文件结构已知(字符串和每行数)

要在文件中查找数字,您需要将文件内容解析为所谓的 “令牌”。 示例:字符串“foo 10 0.1”将使用空白字符作为分隔符分解为“foo”,“10”,“0.1”。此过程称为标记化。 接下来,您需要检查每个令牌是否代表有效数字,为此,只需检查每个字符是否为有效整数。

原创amswer:

您基本上需要执行以下操作

  1. 将文件加载到表示文件逻辑结构的数据结构中(例如std :: map用于存储名称和编号)。这涉及解析文件内容。你可以使用stringstream。
  2. 修改内存中的数据(例如数据[“蝙蝠侠”] + = 100;将“100”添加到与“蝙蝠侠”相关联的数字)
  3. 以rquested格式
  4. 将数据结构存储在输出文件中

    如果输入文件必须用作输出文件,只需重写打开标记(默认为afaik)

答案 1 :(得分:1)

我更改了代码以正确处理文件,您应该能够测试它:

#include "stdafx.h" // for VC++
#include <iostream>
#include <fstream>
#include <string>

#define score_file "d:\\StudentRecord.txt"
#define score_file_bak score_file".bak"
#define score_file_new score_file".new"


using namespace std;

void addScore(string name, double score) 
{
    ofstream outStream;
    outStream.open(score_file, ios_base::app);
    outStream << name << "||" << score << endl;
    outStream.close();
}


void modifyScore(string name, double score) {
    string line;
    int fd =0;
    ifstream scores(score_file);
    if(!scores.is_open()){
        std::cout << "Unable to read score file" << std::endl;
        return ;
    }
    ofstream outStream(score_file_new);
    if(!outStream.is_open()){ 
        std::cout << "Unable to open temp file to write" << std::endl;
        return ;
    }
    name += "||";
    while(getline(scores, line)){
        if(line.find(name) == 0){
            std::cout << "Found: " << line << std::endl;
            outStream << name << score << std::endl;
            fd++;
        }
        else{
            outStream << line << std::endl;
        }
    }
    scores.close();
    outStream.close();
    if(fd<1){ 
        remove(score_file_new);
        std::cout << "Not found: " << name << std::endl;
    }
    else{
        remove(score_file_bak);// delete previous backup
        if(0 != rename(score_file,score_file_bak)){
            std::cout << "Unable to backup" << std::endl;
            remove(score_file_new);  // delete the new file since update failed
            return;
        }
        if(0 == rename(score_file_new,score_file)) 
            std::cout << "Updated succeeded." << std::endl;
        else
            std::cout << "Unable to update\n" << std::endl;
    }
}

void showScore()
{
    string line;
    ifstream scores(score_file);
    while(getline(scores, line)){
        cout << line << std::endl;
    }
    scores.close();
}

int main()
{
    bool reset_scores = true; //do you want to reset all scores?

    if(reset_scores) // clean all old records?  
    {
        ofstream outStream(score_file, ios_base::trunc);outStream.close();
        addScore("Tom",120); addScore("Jerry", 130); addScore("Tim",100);
    }

    std::cout << "Here are the scores" << std::endl;
    showScore();

    std::cout << "Try to change Timmy's score:" << std::endl;
    modifyScore("Timmy",200);
    showScore();

    std::cout << "Try to change Jerry's score:" << std::endl;
    modifyScore("Jerry",1190);
    showScore();

    if(1)
    {
        string aaa; 
        std::cout << "Press enter to quit:\n";
        getline(cin, aaa);
    }
    return 0;
}

我有这个输出:

 Here are the scores
 Tom||120
 Jerry||130
 Tim||100
 Try to change Timmy's score:
 Not found: Timmy||
 Tom||120
 Jerry||130
 Tim||100
 Try to change Jerry's score:
 Found: Jerry||130
 Updated succeeded.
 Tom||120
 Jerry||1190
 Tim||100
 Press enter to quit:

您需要了解如何自己处理输入。

答案 2 :(得分:0)

你不能在他们的中间修改files“;也就是说,您只能截断或附加到文件,或者用另一个字节(相同长度)字节覆盖字节的子序列。

所以你可以按照RedAgito's anser的建议读取内存中的整个文件,修改内存中的表示,然后重写完整整个文件。

当然,这种方法不是很scalable(但实际上是值得的:笔记本电脑有几千兆字节的RAM,而一千兆字节的文本文件将包含数百万个名字 - 数字行,这通常足够了):单个修改的复杂性与行数成正比(除非你知道所有数字都是相同的宽度 - 例如一些电话号码;然后你可以覆盖该行,但你仍然需要找到那条匹配的行)

实用的方法可能是使用某个数据库(可能像sqlite库一样简单;或者像PostGreSQLMongoDB这样的全功能DBMS,或者某些索引文件库(如GDBMkyotocabinet)。

另一种方法可能是使用一些传统的文本结构化格式,如JSONYAML(或者可能是XML,不太适合您的任务)。存在许多能够容易地处理这种格式的C ++库(例如jsoncpp和许多其他)。然后,您将解析完整文件(在几个C ++语句中),可能作为单个(但很大)的JSON对象,然后修改内存中的数据,然后再次写入。

如果您不能使用其中任何一项(例如,如果它是一些家庭作业,而您的老师正在限制您),您可能会编写自己的例程来管理固定大小的记录(并且可能在它们之上提供一些更高级别的抽象)是一些标记的联合可能包含其他记录的偏移量(这基本上是数据库系统和索引文件正在做的事情)。如果你被限制使用文本文件(这是一个好主意;你最好有你可以用任何好的编辑器修改的数据,àlaemacsvim),那么你必须解析整个文件,将其表示存储在内存中(例如,在您的情况下可能是一些std::map<std::string,long>,密钥是名称),然后重写整个文件。