通过扫描关键字来读取文本文件

时间:2014-04-25 13:48:54

标签: c++ string file-io io ifstream

作为更大的应用程序的一部分,我正在研究一个类,用于从文本文件中读取输入,以便在程序初始化时使用。现在我自己对编程很新,而且我在12月才开始学习C ++,所以我非常感谢有关如何开始的一些提示和想法!我提前为一段相当长的文字道歉。

文本文件格式为"关键字驱动"通过以下方式:

  • 需要以给定顺序编写的主要/部分关键字(目前为8个)的数量相当少。其中一些是可选的,但如果包含它们,则应遵守给定的顺序。

示例:

假设有3个潜在的关键字,如下所示:

"KEY1" (required)
"KEY2" (optional)
"KEY3" (required) 

如果输入文件仅包含所需的输入文件,则顺序应为:

"KEY1"
"KEY3"

否则应该是:

"KEY1"
"KEY2"
"KEY3"
  • 如果存在所有必需的关键字,并且总排序正常,程序应继续阅读订单给出的序列中的每个部分。

  • 每个部分都会包含一个(可能很大)数量的子关键字,其中一些是可选的,其中一些不是,但这里的顺序并不重要。

  • 以字符'*''--'开头的行表示注释行,应忽略它们(以及空行)。

  • 包含关键字的行应该(最好)包含除关键字以外的其他内容。至少,关键字必须是出现在那里的第一个单词。

我已经实现了部分框架,但我觉得到目前为止我的方法一直是临时性的。目前我已经为每个section / main关键字手动创建了一个方法,该程序的第一个任务是扫描文件以找到这些关键字并将必要的信息传递给方法。

我首先使用std::ifstream对象扫描文件,删除空行和/或注释行,并将其余行存储在std::vector<std::string>类型的对象中。

你认为这是一个好方法吗?

此外,我存储了每个关键字在此向量中开始和停止(在两个整数数组中)的索引。这是上述方法的输入,它看起来像这样:

bool readMAINKEY(int start, int stop);

现在我已经做到了这一点,即使我觉得它不是很优雅,我想我可以暂时保留它。

但是,我觉得我需要一个更好的方法来处理每个部分内部的阅读,我的主要问题是我应该如何在这里存储关键字?它们是否应该作为数组存储在一个数组中输入类中的本地命名空间,或者可能是类中的静态变量?或者它们应该在相关功能内局部定义?我应该使用枚举吗?问题很多!

现在我开始在每个readMAINKEY()方法中本地定义子关键字,但我发现这不是最优的。理想情况下,我希望在每个方法中重用尽可能多的代码,调用一个通用的readSECTION()方法,而我当前的方法似乎会导致很多代码重复和编程错误的可能性。我想最明智的做法就是删除所有(目前为8个)不同的readMAINKEY()方法,并使用相同的函数来处理各种关键字。也有可能有子关键字等(即更通用的嵌套方法),所以我想也许这是要走的路,但我不确定如何最好地实现它?

  • 一旦我在&#34;底层&#34;处理了关键字,程序将根据实际关键字预期以下行的特定格式。原则上,每个关键字的处理方式都不同,但这里也有可能通过定义不同的&#34;类型来解决一些代码问题。关键字取决于程序在触发读取后期望执行的操作。常见任务包括例如解析整数或双数组,但原则上它可以是任何东西!

  • 如果由于某种原因导致关键字无法正确处理,程序应尽可能尝试使用默认值而不是终止程序(如果合理),但应将错误消息写入日志文件。对于可选关键字,当然也会使用默认值。

因此,为了总结,我的主要问题如下:

1。你认为我认为我在std::vector<std::string>中存储相关行的方法是合理的吗?

这当然要求我做很多&#34;索引工作&#34;跟踪不同关键字所在的向量中的位置。或者我应该更多地工作&#34;直接&#34;使用原始std::ifstream对象?或其他什么?

2。鉴于这样的矢量存储文本文件的行,我怎样才能最好地检测关键字并开始阅读它们之后的信息?

在这里,我需要考虑可能的排序以及是否需要关键字。另外,我需要检查每个&#34;底层&#34;之后的行。关键字的格式为每种情况下预期的格式。

我有一个想法是将关键字存储在不同的容器中,具体取决于它们是否是可选的(或者可能使用std::map<std::string,bool>类型的对象),然后将它们从容器如果处理得当,但我不确定应该怎么做......

我想有一千种不同的方式可以回答这些问题,但如果有经验的人可以分享一些关于如何继续的想法,我将不胜感激。有没有,例如a&#34;标准&#34;做这种事的方式?当然,很多细节也取决于具体的应用,但我认为这里指出的一般格式可以在很多不同的应用程序中使用,如果以一种好的方式进行编程,不需要很多修改!

更新

好的,让我试着更具体一点。我当前的应用程序应该是一个油藏模拟器,因此作为输入的一部分,我需要有关网格/网格,岩石和流体属性,整个模拟中的井/边界条件等信息。目前,我一直在考虑在输入时使用(几乎)与商业Eclipse模拟器相同的设置,详细信息请参阅 http://petrofaq.org/wiki/Eclipse_Input_Data

然而,我可能会稍微改变一下,所以没有什么是一成不变的。此外,我有兴趣制作一个更通用的&#34; KeywordReader&#34;经过轻微修改的类也可以适用于其他应用程序,至少可以在合理的时间内完成。

例如,我可以发布执行文本文件初始扫描的当前代码,并找到主要关键字的位置。正如我所说,我不太喜欢我的解决方案,但它似乎适用于它需要做的事情。

在.cpp文件的顶部,我有以下命名空间:

//Keywords used for reading input:
namespace KEYWORDS{

    /*
    * Main keywords and corresponding boolean values to signify whether or not they are required as input.
    */
    enum MKEY{RUNSPEC = 0, GRID = 1, EDIT = 2, PROPS = 3, REGIONS = 4, SOLUTION = 5, SUMMARY =6, SCHEDULE = 7};
    std::string mainKeywords[] = {std::string("RUNSPEC"), std::string("GRID"), std::string("EDIT"), std::string("PROPS"),
        std::string("REGIONS"), std::string("SOLUTION"), std::string("SUMMARY"), std::string("SCHEDULE")};
    bool required[] = {true,true,false,true,false,true,false,true};
    const int n_key = 8;

}//end KEYWORDS namespace

然后再往下我有以下功能。我不确定它是多么容易理解..

bool InputReader::scanForMainKeywords(){

    logfile << "Opening file.." << std::endl;

    std::ifstream infile(filename);

    //Test if file was opened. If not, write error message:
    if(!infile.is_open()){
        logfile << "ERROR: Could not open file! Unable to proceed!" << std::endl;
        std::cout << "ERROR: Could not open file! Unable to proceed!" << std::endl;
        return false;
    }

    else{

        logfile << "Scanning for main keywords..." << std::endl;

        int nkey = KEYWORDS::n_key;

        //Initially no keywords have been found:
        startIndex = std::vector<int>(nkey, -1);
        stopIndex = std::vector<int>(nkey, -1);

        //Variable used to control that the keywords are written in the correct order:
        int foundIndex = -1;

        //STATISTICS:
        int lineCount = 0;//number of non-comment lines in text file
        int commentCount = 0;//number of commented lines in text file
        int emptyCount = 0;//number of empty lines in text file

        //Create lines vector:
        lines = std::vector<std::string>();

        //Remove comments and empty lines from text file and store the result in the variable file_lines:
        std::string str;
        while(std::getline(infile,str)){
            if(str.size()>=1 && str.at(0)=='*'){
                commentCount++;
            }
            else if(str.size()>=2 && str.at(0)=='-' && str.at(1)=='-'){
                commentCount++;
            }
            else if(str.size()==0){
                emptyCount++;
            }
            else{
                //Found a non-empty, non-comment line.
                lines.push_back(str);//store in std::vector
                //Start by checking if the first word of the line is one of the main keywords. If so, store the location of the keyword:
                std::string fw = IO::getFirstWord(str);

                for(int i=0;i<nkey;i++){
                    if(fw.compare(KEYWORDS::mainKeywords[i])==0){
                        if(i > foundIndex){
                            //Found a valid keyword!
                            foundIndex = i;
                            startIndex[i] = lineCount;//store where the keyword was found!
                            //logfile << "Keyword " << fw << " found at line " << lineCount << " in lines array!" << std::endl;
                            //std::cout << "Keyword " << fw << " found at line " << lineCount << " in lines array!" << std::endl;
                            break;//fw cannot equal several different keywords at the same time!
                        }
                        else{
                            //we have found a keyword, but in the wrong order... Terminate program:
                            std::cout << "ERROR: Keywords have been entered in the wrong order or been repeated! Cannot continue initialisation!" << std::endl;
                            logfile << "ERROR: Keywords have been entered in the wrong order or been repeated! Cannot continue initialisation!" << std::endl;
                            return false;
                        }
                    }
                }//end for loop

                lineCount++;
            }//end else (found non-comment, non-empty line)
        }//end while (reading ifstream)

        logfile <<  "\n";
        logfile << "FILE STATISTICS:" << std::endl;
        logfile << "Number of commented lines: " << commentCount << std::endl;
        logfile << "Number of non-commented lines: " << lineCount << std::endl;
        logfile << "Number of empty lines: " << emptyCount << std::endl;
        logfile << "\n";


        /*
        Print lines vector to screen:
        for(int i=0;i<lines.size();i++){
            std:: cout << "Line nr. " << i << " : " << lines[i] << std::endl;
        }*/

        /*
        * So far, no keywords have been entered in the wrong order, but have all the necessary ones been found?
        * Otherwise return false.
        */

        for(int i=0;i<nkey;i++){
            if(KEYWORDS::required[i] && startIndex[i] == -1){
                logfile << "ERROR: Incorrect input of required keywords! At least " << KEYWORDS::mainKeywords[i] << " is missing!" << std::endl;;
                logfile << "Cannot proceed with initialisation!" << std::endl;
                std::cout << "ERROR: Incorrect input of required keywords! At least " << KEYWORDS::mainKeywords[i] << " is missing!" << std::endl;
                std::cout << "Cannot proceed with initialisation!" << std::endl;
                return false;
            }
        }

        //If everything is in order, we also initialise the stopIndex array correctly:

        int counter = 0;

        //Find first existing keyword:
        while(counter < nkey && startIndex[counter] == -1){
            //Keyword doesn't exist. Leave stopindex at -1!
            counter++;
        }

        //Store stop index of each keyword:
        while(counter<nkey){

            int offset = 1;

            //Find next existing keyword:
            while(counter+offset < nkey && startIndex[counter+offset] == -1){
                offset++;
            }


            if(counter+offset < nkey){
                stopIndex[counter] = startIndex[counter+offset]-1;
            }
            else{
                //reached the end of array!
                stopIndex[counter] = lines.size()-1;
            }

            counter += offset;
        }//end while

        /*
        //Print out start/stop-index arrays to screen:
        for(int i=0;i<nkey;i++){
            std::cout << "Start index of " << KEYWORDS::mainKeywords[i] << " is : " << startIndex[i] << std::endl;
            std::cout << "Stop index of " << KEYWORDS::mainKeywords[i] << " is : " << stopIndex[i] << std::endl;
        }
        */

        return true;

    }//end else (file opened properly)
}//end scanForMainKeywords()

1 个答案:

答案 0 :(得分:0)

您说您的目的是从文本文件中读取初始化数据。 似乎您需要解析(语法分析)此文件并将数据存储在正确的密钥下。

如果语法是固定的并且每个构造都以关键字开头,那么您可以编写一个递归下降(LL1)解析器来创建树(每个节点都是子分支的stl向量)来存储数据。

如果语法是免费的,您可以选择JSON或XML并使用现有的解析库。