读取输入文件的各个部分

时间:2009-04-14 00:28:33

标签: c++ parsing file-io

我想在C ++中读取一个输入文件,其结构(或缺少)类似于 text = number 的一系列行,例如

input1 = 10
input2 = 4
set1 = 1.2
set2 = 1.e3

我想把这个号码拿出来,把剩下的号码扔掉。数字可以是整数或双数,但我知道它们是一个还是其他。

我也想阅读它,例如

input1 =    10
input2=4
set1   =1.2
set2= 1.e3

以便对用户更加健壮。我认为这意味着它不应该是格式化的红色。

无论如何,有没有明智的方法呢?

我已经尝试了以下内容,但对我一直在做的事情知之甚少,结果如预期的那样......没有成功。

    #include <stdio.h>
    #include <stdlib.h>
    #include <float.h>
    #include <math.h>
    #include <iostream>
    #include <fstream>
    #include <iomanip>
    #include <cstdlib>
    #include <boost/lexical_cast.hpp>
    #include <string>

    using namespace std;
    using namespace boost;

    int main(){

            string tmp;
            char temp[100];

            int i,j,k;

            ifstream InFile("input.dat");

            //strtol
            InFile.getline(temp,100);
            k=strtol(temp,0,10);
            cout << k << endl;

            //lexical_cast
            InFile.getline(temp,100);
            j = lexical_cast<int>(temp);
            cout << j << endl;

            //Direct read
            InFile >> tmp >> i;
            cout << i << endl;

            return 0;
    }

9 个答案:

答案 0 :(得分:6)

一次只读一行 然后在'='符号上分割每一行。使用流功能完成剩下的工作。

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

int main()
{
    std::ifstream    data("input.dat");
    std::string      line;

    while(std::getline(data,line))
    {
        std::stringstream    str(line);
        std::string          text;

        std::getline(str,text,'=');

        double   value;
        str >> value;
    }
}

进行错误检查:

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

int main()
{
    std::ifstream    data("input.dat");
    std::string      line;

    while(std::getline(data,line))
    {
        std::stringstream    str(line);
        std::string          text;
        double               value;

        if ((std::getline(str,text,'=')) &&  (str >> value))
        {
            // Happy Days..
            // Do processing.
            continue; // To start next iteration of loop.
        }
        // If we get here. An error occurred.
        // By doing nothing the line will be ignored.
        // Maybe just log an error.
    }
}

答案 1 :(得分:5)

这里已经有一些很好的解决方案了。然而,只是为了抛弃它,一些评论意味着Boost Spirit对于这个问题是不恰当的解决方案。我不确定我是否完全不同意。但是,以下解决方案非常简洁,可读(如果您知道EBNF)并且容错。我考虑使用它。

#include <fstream>
#include <string>
#include <boost/spirit.hpp>

using namespace std;
using namespace boost::spirit;

int main()
{
    ifstream       data("input.dat");
    string         line;
    vector<double> numbers;

    while(getline(data,line))
    {
        parse(line.c_str(), 
            *(+~ch_p('=') >> ch_p('=') >> real_p[push_back_a(numbers)]), 
            space_p);
    }
}

答案 2 :(得分:2)

C FTW(经过修改以处理双打)

#include <stdio.h>

int
main ()
{
    double num;

    while (!feof (stdin))
         if (1 == fscanf (stdin, "%*[^=] = %lf", &num))
            printf ("%g\n", num);

    return 0;
}

答案 3 :(得分:2)

我是否做对了:你拿线,从中提取数字并将其打印出来(或者在其他地方使用它,无论如何)?如果是这样,你可以使用字符串函数和istringstream:

//these are your codes, removing some though
#include <stdio.h>
#include <stdlib.h>
#include <float.h>
#include <math.h>
#include <iostream>
#include <fstream>
#include <string>
//adding
#include <sstream>

using namespace std;

int main()
{
string line="", extract="";
int placeofop=0, input;
ifstream InFile("input.dat");

while(!InFile.eof())
{
     getline(InFile, line);
     placeofop = line.find("=");
     extract = line.substr(placeofop, (line.length()-placeofop));
     istringstream trythis(extract);
     trythis >> input;
     cout << input << endl;
}

return 0;
}  

我不是百分百确定我是否记得正确的功能,但是如果我把它们全部正确的话,这应该有用。

编辑:我意识到我可以编辑我的帖子:)我在评论中反转了&lt;&lt;&lt;&lt;&lt;&lt;&gt;&gt;&gt;我一直在做的事情......现在没有编译错误。

答案 4 :(得分:2)

脱离我的头顶:

vector<double> vals(istream &in) {
    vector<double> r;
    string line;

    while (getline(f, line)) {
        const size_t eq = line.find('=');
        if (eq != string::npos) {
            istringstream ss(line.substr(eq + 1));
            double d = 0;
            ss >> d;
            if (ss) r.push_back(d);
            else throw "Line contains no value";
        }
        else {
            throw "Line contains no =";
        }
    }

    return r;
}

int main(int argc, char *argv[]) {
    vector<double> vs = vals(ifstream(argv[1]));
}

答案 5 :(得分:1)

现在您已经在lexical_cast中使用了boost,只需将boost::split()boost::is_any_of()的每一行解析为1个2元素向量,并启用token_compress。

以下代码说明了解析,但跳过了数字转换,可以使用boost lexical_cast轻松解决。

#include <fstream>
#include <sstream>
#include <string>
#include <iostream>
#include <vector>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/foreach.hpp>  

using std::string;
using std::cout;
using std::ifstream;
using std::stringstream;  
using std::vector; 

std::string file_to_string()
{
    ifstream    data("data.txt");
    stringstream s;
    s << data.rdbuf();
    return s.str();
}

void print_parameter(vector<string>& v)
{
    cout << v_para[0];
    cout << "=";
    cout << v_para[1];
    cout << std::endl;   
}

vector<string> string_to_lines(const string& s)
{
    return  v_lines;
}


int main()
{

    vector<string> v_lines;
    boost::split(v_lines, file_to_string(), boost::is_any_of("\n"), boost::token_compress_on);

    vector<string> v_para;
    BOOST_FOREACH(string& line, v_lines)
    {
        if(line.empty()) continue;

        boost::split(v_para, line, boost::is_any_of(" ="), boost::token_compress_on);

        // test it
        print_parameter(v_para);
    }
}

答案 6 :(得分:1)

如果您正在设计此格式,我建议采用INI file格式。 轻量级语法INI格式包括部分(允许您在格式中具有更多结构),在您的情况下可能需要或可能不需要:

[section_1] 
variable_1=value1
variable_2=999 
[sectionA]
variable_A=value A 
variable_B=111

此维基百科页面上的外部链接列出了许多库,这些库可用于处理这些类型的文件,这些文件从Windows API扩展/替换基本的GetPrivateProfileString函数并支持其他平台。 其中大多数会处理空间填充=符号(或至少在=之前,因为=之后的空格可能是有意/重要的。 如果您不希望这些库中的某些库也可能有省略[sections]的选项(我自己的C ++类用于处理类似于格式文件的INI具有此选项)。

这些库和/或使用Windows API GetPrivateProfileXXX函数的优点是您的程序可以访问特定变量 (即,在没有程序必须的情况下,从sectionA获取或设置variable_A的值) 写/扫描/重写整个文件。

答案 7 :(得分:0)

这是我最快捷的STL解决方案:

#include <fstream>
#include <list>
#include <locale>
void foo()
{
std::fstream f("c:\\temp\\foo.txt", std::ios_base::in);
std::list<double> numbers;
while (!f.eof())
{   
    int c = f.get();
    if (std::isdigit(c, std::locale::classic()) ||
        c == '+' ||
        c == '-' ||
        c == '.')
    {
        f.putback(c);
        double val;
        f >> val;
        if (f.fail()) {
            f.clear(f.eof() ? std::ios_base::eofbit : std::ios_base::goodbit);
            continue;
        }
        else
        {
            numbers.push_back(val);
        }           
    }
}
}

答案 8 :(得分:0)

刚测试过这个......它可以工作,并且不需要C ++标准库以外的任何东西。

#include <iostream>
#include <map>
#include <string>
#include <algorithm>
#include <iterator>
#include <cctype>
#include <sstream>

using namespace std; // just because this is an example...

static void print(const pair<string, double> &p)
{
    cout << p.first << " = " << p.second << "\n";
}

static double to_double(const string &s)
{
    double value = 0;
    istringstream is(s);
    is >> value;
    return value;
}

static string trim(const string &s)
{
    size_t b = 0;
    size_t e = s.size();
    while (b < e && isspace(s[b])) ++b;
    while (e > b && isspace(s[e-1])) --e;
    return s.substr(b, e - b);
}

static void readINI(istream &is, map<string, double> &values)
{
    string key;
    string value;

    while (getline(is, key, '='))
    {
        getline(is, value, '\n');
        values.insert(make_pair(trim(key), to_double(value)));
    }
}

int main()
{
    map<string, double> values;
    readINI(cin, values);
    for_each(values.begin(), values.end(), print);
    return 0;
}
编辑:我刚刚阅读了原始问题,并注意到我没有提出确切的答案。如果您不关心密钥名称,juts会丢弃它们。另外,为什么需要确定整数值和浮点值之间的差异? 1000是整数还是浮点数?那么1e3还是1000.0呢?检查给定的浮点值是否为整数是很容易的,但是有一个数字的clas既是有效整数又是有效的浮点值,如果你想处理,你需要进入自己的解析例程这是正确的。