Чтение частей входного файла - PullRequest
2 голосов
/ 14 апреля 2009

Я хотел бы прочитать входной файл на 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 ]

6 голосов
/ 14 апреля 2009

Просто прочитайте по одной строке за раз.
Затем разделите каждую строку на знак «=». Используйте функциональность потока, сделайте все остальное.

#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.
    }
}
5 голосов
/ 23 апреля 2009

Здесь уже есть несколько хороших решений. Тем не менее, просто чтобы высказать это, некоторые комментарии подразумевают, что 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 голосов
/ 14 апреля 2009

с макушки головы:

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]));
}
2 голосов
/ 14 апреля 2009

Правильно ли я понимаю: вы берете строку, извлекаете из нее число и распечатываете ее (или используете где-то еще, что угодно)? Если это так, вы можете использовать строковые функции и 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;
}  

Я не уверен на 100%, правильно ли я помню функции, но это должно сработать, если я все понял правильно.

edit: я понял, что могу редактировать свои сообщения :) Я поменял местами << и >>, как в комментарии; что-то, что я продолжаю делать все время ... Теперь не дает ошибок компиляции.

2 голосов
/ 14 апреля 2009

C FTW (модифицировано для обработки двойников)

#include <stdio.h>

int
main ()
{
    double num;

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

    return 0;
}
1 голос
/ 18 апреля 2009

Если вы разрабатываете этот формат, я бы предложил принять формат INI . Облегченный синтаксический формат INI включает разделы (позволяет вам иметь немного больше структуры в формате), что может или не может быть желательным в вашем случае:

* 1005 Т.е. *

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

Внешние ссылки на этой странице википедии содержат список библиотек, которые можно использовать для работы с файлами такого типа, которые расширяют / заменяют базовые функции GetPrivateProfileString из Windows API и поддерживают другие платформы. Большинство из них будут обрабатывать знак пробела = (или, по крайней мере, перед =, поскольку пробел после = может быть намеренным / значительным). Некоторые из этих библиотек могут также иметь возможность опустить [section], если вы этого не хотите (у моего собственного класса C ++ для обработки INI-подобных файлов форматирования есть эта опция).

Преимущество этих библиотек и / или использования функций Windows API GetPrivateProfileXXX заключается в том, что ваша программа может обращаться к определенным переменным (Т.е. получить или установить значение для variable_A из секции A) без необходимости в вашей программе запись / сканирование / перезапись всего файла.

1 голос
/ 14 апреля 2009

теперь, когда вы уже используете boost с lexical_cast, просто проанализируйте каждую строку с 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);
    }
}
0 голосов
/ 14 апреля 2009

Только что проверил это ... это работает, и не требует ничего вне стандартной библиотеки 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;
}

РЕДАКТИРОВАТЬ: Я только что прочитал оригинальный вопрос и заметил, что не дает точного ответа. Если вас не интересуют названия клавиш, просто выбросьте их. Кроме того, зачем вам нужно определять разницу между целочисленными значениями и значениями с плавающей точкой? 1000 целое число или число с плавающей точкой? А как насчет 1e3 или 1000.0? Достаточно просто проверить, является ли данное значение с плавающей точкой целочисленным, но есть классы чисел, которые являются как действительными целыми числами, так и действительными значениями с плавающей точкой, и вам нужно войти в свои собственные процедуры синтаксического анализа, если вы хотите иметь дело с это правильно.

0 голосов
/ 14 апреля 2009

Вот мое самое быстрое решение 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);
        }           
    }
}
}
...