2 Stimmen

Lesen von Teilen einer Eingabedatei

Ich möchte eine Eingabedatei in C++ lesen, deren Struktur (oder das Fehlen einer solchen) in etwa aus einer Reihe von Zeilen mit Text = Zahl , wie zum Beispiel

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

Ich möchte die Nummer aus der Zeile nehmen und den Rest wegwerfen. Zahlen können entweder ganze Zahlen oder Doppelzahlen sein, aber ich weiß, wann sie das eine oder das andere sind.

Ich würde es auch gerne lesen, wie

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

um für den Benutzer robuster zu sein. Ich denke, das bedeutet, dass sie nicht in formatierter Form rot sein sollte.

Wie auch immer, gibt es eine intelligente Möglichkeit, das zu tun?

Ich habe bereits das Folgende versucht, aber mit minimalen Kenntnissen über das, was ich getan habe, so dass das Ergebnis wie erwartet war... kein Erfolg.

    #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;
    }

1voto

t.g. Punkte 1649

Da Sie jetzt bereits boost mit lexical_cast verwenden, parsen Sie einfach jede Zeile mit boost::split() y boost::is_any_of() in 1 2-Element-Vektor, wobei token_compress eingeschaltet ist.

Der folgende Code veranschaulicht das Parsen, überspringt aber die numerische Umwandlung, die mit boost lexical_cast leicht gelöst werden könnte.

#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);
    }
}

1voto

Roger Nelson Punkte 1854

Wenn Sie dieses Format entwerfen, würde ich vorschlagen, dass Sie die INI-Datei Format. Das leichtgewichtige syntaktische INI-Format enthält Abschnitte (die es Ihnen ermöglichen, das Format etwas mehr zu strukturieren), was in Ihrem Fall wünschenswert sein kann oder auch nicht:

D.h.

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

Die externen Links auf dieser Wikipedia-Seite führen eine Reihe von Bibliotheken auf, die für die Arbeit mit dieser Art von Dateien verwendet werden können und die die grundlegenden GetPrivateProfileString-Funktionen der Windows-API erweitern/ersetzen und andere Plattformen unterstützen. Die meisten dieser Bibliotheken verarbeiten das mit Leerzeichen aufgefüllte =-Zeichen (oder zumindest vor dem =, da ein Leerzeichen nach dem = absichtlich/bedeutend sein kann. Einige dieser Bibliotheken könnten auch eine Option zum Weglassen von [sections] haben, wenn Sie das nicht wollen (meine eigene C++-Klasse für die Handhabung von INI-ähnlichen Formatdateien hat diese Option).

Der Vorteil dieser Bibliotheken und/oder der Verwendung der Windows-API-Funktionen GetPrivateProfileXXX ist, dass Ihr Programm auf bestimmte Variablen zugreifen kann (z.B. den Wert für Variable_A aus AbschnittA abrufen oder setzen), ohne dass Ihr Programm die gesamte Datei schreiben/scannen/umschreiben muss.

0voto

David Gladfelter Punkte 4107

Hier ist meine schnellste STL-Lösung:

#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);
        }           
    }
}
}

0voto

Tom Punkte 10491

Gerade getestet dies... es funktioniert, und erfordert nichts außerhalb der C++-Standardbibliothek.

#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;
}

EDIT: Ich habe gerade die ursprüngliche Frage gelesen und festgestellt, dass ich keine genaue Antwort gebe. Wenn Sie sich nicht für die Schlüsselnamen interessieren, können Sie sie einfach weglassen. Warum müssen Sie außerdem den Unterschied zwischen Ganzzahl- und Gleitkommawerten erkennen? Ist 1000 eine Ganzzahl oder ein Float? Was ist mit 1e3 o 1000.0 ? Es ist einfach genug zu prüfen, ob ein bestimmter Fließkommawert ganzzahlig ist, aber es gibt eine Gruppe von Zahlen, die sowohl gültige Ganzzahlen als auch gültige Fließkommawerte sind, und Sie müssen Ihre eigenen Parsing-Routinen entwickeln, wenn Sie damit richtig umgehen wollen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X