2013-04-24 10 views
5

Ich habe Probleme beim Lesen von einer Eingabedatei. Die Eingangsdatei wie dieserLesen von Eingabedatei

Lionel Messi -10 43

Fernando Torres 9 -29

Cristiano Ronaldo 7 31

Wayne Rooney 10 37

Neymar 17 29

sieht

Andres Iniesta 8 32

Robin van Persie 19 20

Lionel Messi 10 43

Xavi Hernandez 6 36

Mesut Özil 10 38

Didier Drogba 10 35

Fernando Torres 9 29

Kaká 10 17

Das Problem ist, dass ich die Funktion getline nicht verwenden kann, weil ich den Namen in eine einzelne Variable speichern möchte, um sie in einem Array zu speichern, und die erste Zahl in eine Variable und die zweite in eine andere Variable. Ich habe auch versucht, die Peek-Funktion zu benutzen, aber ich habe das nie gelernt, also hatte ich keinen Erfolg damit. Wenn jemand weiß, wie bis zum Ende des Namens zu lesen und speichern Sie es in einer einzigen Variablen, die sehr geschätzt werden würde. Diese

ist, was mein Code wie folgt aussieht, wenn im aus der Eingabedatei lesen

while(!fin.eof()) 
    { 

    fin >> first >> last >> num >> point; 

    if (num > 0 && point > 0) 
    { 
      list[i].firstname = first; 
      list[i].lastname = last; 
      list[i].number = num; 
      list[i].points = point; 
      i++; 
    } 
    else if (num < 0 || point < 0) 
    { 
      reject[j].firstname = first; 
      reject[j].lastname = last; 
      reject[j].number = num; 
      reject[j].points = point; 
      j++; 
    } 

    } 

Das funktioniert perfekt, wenn die Eingabe einen ersten und einen Nachnamen hat. Ich weiß, das Problem ist auf der fin >> first >> last >> num >> point;

aber ich bin nicht ganz sicher, wie erste Stelle setzen und die letzten (und möglicherweise Mitte) zusammen

+4

Ah, aber Sie können 'getline' verwenden. – chris

+1

Ist der "Abstand" zwischen den Teilen von Namen der gleiche wie zwischen dem letzten Namen und den Zahlen? Es wäre sehr hilfreich, wenn zwischen den letzten Namen und den Zahlen ein Tab-Zeichen "\ t" steht. Sonst wollen Sie (wahrscheinlich) eine ganze Zeile lesen und dann die Zahlenbits von der Rückseite des Strings entfernen (zum Beispiel mit 'std :: string :: find_last_of' und' std :: string :: substr')) –

+0

Ja, alle Leerzeichen sind zwischen den Namen und Nummern gleich. Also ist es möglich, die ganze Zeile in einer String-Variablen zu speichern und dann die zwei Zahlen vom Ende der Zeichenfolge zu entfernen und sie in separate Variablen zu setzen? – kylekolossus

Antwort

0

Eigentlich kann man verwenden getline(); Verwenden Sie die Methode mit zwei Parametern und übergeben Sie ihr einen Stream und '\ n' als Trennzeichen.

Weisen Sie die gesamte Zeile einer Zeichenkette zu, teilen Sie die Zeichenkette dann mit einem Leerzeichen als Trennzeichen, und konvertieren Sie die zweiten beiden Zeichen in Ganzzahlen.

+0

Space schlägt auf Leerzeichen fehl im Namen (der pro Name variabel ist) –

+0

Aktualisierte Antwort, um das zu adressieren. –

+0

@MichaelDorgan Es ist immer noch so möglich ... benutze einfach die letzten beiden Elemente als Ganzzahlen und alle anderen Elemente (egal wie viele) als Namen. Gegeben ist eine feste Menge von ganzen Zahlen nach jedem Namen. –

4

können Sie std::getline verwenden, um die Linien zu extrahieren, analysieren die die Zeile in einen std::vector von Leerzeichen getrennt Worte. Dann wissen Sie, dass words.size() - 2 der Wörter Teil des Namens sind. Zum Beispiel:

std::fstream in("in.txt"); 
std::string line; 

// Extract each line from the file 
while (std::getline(in, line)) { 
    std::istringstream line_stream(line); 

    // Now parse line_stream into a vector of words 
    std::vector<std::string> words(std::istream_iterator<std::string>(line_stream), 
           (std::istream_iterator<std::string>())); 

    int name_word_count = words.size() - 2; 
    if (name_word_count > 0) { 
    // Concatenate the first name_word_count words into a name string 
    // and parse the last two words as integers 
    } 
} 
+0

Das ist keine schlechte Lösung. Ich hatte nicht daran gedacht, dass '>>' in eine Zeichenkette Leerzeichen als Trennzeichen verwendet. (Natürlich, wie du es geschrieben hast, wird es nicht kompilieren. Aber du bist nicht der erste, der dem peinlichsten Parse zum Opfer fällt. Schließe ein (oder beide) der Argumente an den Konstruktor von 'words' mit eine zusätzliche Reihe von Klammern –

+0

@JamesKanze Hm, ich frage mich, warum das passiert ist.Aber sicher kann das erste Argument nicht möglich sein, ein Typ (es hat 'line_stream' drin)? –

+0

' line_stream' wird als interpretiert Name des ersten Parameters –

0

1.) Öffnen Sie die Datei

std::ifstream myFile("somewhere.txt"); 

2.) Überprüfen Sie, ob die Datei

if(myFile.is_open()) 

3 offen ist.Lesen), bis das Ende der Datei

while(!myFile.eof()) 

4.) Lesen Sie den ersten Namen in einen ersten Namen Array

myFile >> firstName[numberOfPeople]; 

5.) Lesen Sie den Nachnamen in dem Nachnamen Array

myFile >> lastName[numberOfPeople]; 

6.) Lies die ganze Zahl in ein Integer-Array

myFile >> integer[numberOfPeople]; 

7.) Schrittmaßnummer von Menschen

numberOfPeople++; 

8.) Wenn während getan wird, in der Nähe Datei

myFile.close(); 

9.) Wenn die Datei den Fehler nicht geöffnet wurde, berichten.

Sie können die getline-Funktion verwenden, Sie müssen nur die Zeichenfolge analysieren. Dies kann mit strtok erreicht werden.

+2

Sie wollen nie 'strtok' verwenden. –

+0

Und Sie brauchen nicht wirklich das Ende. (Sie tun für die Ausgabe, weil Sie überprüfen müssen, ob es erfolgreich ist, aber für die Eingabe, nicht.) –

+2

Und re "bad code in diesem thread": 'while (! MyFile.eof())' ist definitiv ein Fehler und sehr schlechter Code. –

1

Es sieht für mich aus, wie Sie getline verwenden müssen, und dann die Zeile analysieren. Eine Lösung zum Analysieren könnte sein, die Zeile unmittelbar vor der ersten Ziffer zu teilen, dann die erste Hälfte zu beschneiden und für den Namen zu verwenden und die zweite Hälfte mit einem std::istringstream zu analysieren, um die beiden Zahlen zu lesen. Dies wird natürlich scheitern, , wenn jemand eine Ziffer als Teil ihres Namens hat, aber scheint mir eine legitime Einschränkung zu sein. Mit anderen Worten, für jede Zeile, dann würden Sie tun:

std::string::iterator first_digit 
     = std::find_if(line.begin(), line.end(), IsDigit()); 
if (first_digit == line.end()) { 
    // format error... 
} else { 
    name = trim(std::string(line.begin(), first_digit)); 
    std::istringstream parser(std::string(first_digit, line.end())); 
    parser >> firstNumber >> secondNumber >> std::ws; 
    if (!parser || parser.get() != EOF) { 
     // format error... 
    } else { 
     // Do what ya gotta do. 
    } 
} 
1

So etwas wie dies funktionieren soll:

std::string str; 
while(getline(infile, str)) 
{ 
    std::string::size_type pos; 
    pos = str.find_last_of(' '); 
    if (pos == std::string::npos || pos < 1) 
    { 
     cout << "Something not right with this string: " << str << endl; 
     exit(1); 
    } 
    int last_number = stoi(str.substr(pos)); 
    str = str.substr(0, pos-1); // Remove the number and the space. 
    pos = str.find_last_of(' '); 
    if (pos == std::string::npos || pos < 1) 
    { 
     cout << "Something not right with this string: " << str << endl; 
     exit(1); 
    } 
    int first_number = stoi(str.substr(pos)); 
    str = str.substr(0, pos-1); 
    // str now contains the "name" as one string. 

    // ... here you use last_number and first_number and str to do what you need to do. 
} 
0

ich eine Lösung für Ihr Problem umgesetzt habe (vor allem für mein eigenes Lernen). Es funktioniert wie beabsichtigt, aber es fühlt sich etwas lang an.

#include<string> 
#include<fstream> 
#include<sstream> 
#include<iostream> 
#include<vector> 
#include<algorithm> 
#include<iterator> 

struct PlayerData { 
    std::string name; 
    int number; 
    int points; 
}; 

std::ostream& operator<<(std::ostream& os, const PlayerData& p) { 
    os<<"Name: "<<p.name<<", Number: "<<p.number<<", Points: "<<p.points; 
    return os; 
} 

PlayerData parse(const std::string& line) { 
    PlayerData data; 
    std::stringstream ss(line); 
    std::vector<std::string> tokens; 
    std::copy(std::istream_iterator<std::string>(ss), 
      std::istream_iterator<std::string>(), 
      std::back_inserter<std::vector<std::string>>(tokens)); 
    data.points = std::stoi(tokens.at(tokens.size() - 1)); 
    data.number = std::stoi(tokens.at(tokens.size() - 2)); 
    for(auto it=tokens.begin(); it!=tokens.end()-2; ++it) { 
    data.name.append(" "); 
    data.name.append(*it); 
    } 
    return data; 
} 

int main(int argc, char* argv[]) { 
    std::string line; 
    std::vector<PlayerData> players; 

    { // scope for fp          
    std::ifstream fp(argv[1], std::ios::in); 
    while(!fp.eof()) { 
     std::getline(fp, line); 
     if(line.size()>0) { 
     players.push_back(parse(line)); 
     } 
    } 
    } // end of scope for fp 

    // print list of players, or do whatever you want with it. 
    for(auto p:players) { 
    std::cout<<p<<std::endl;  
    } 

    return 0; 
} 

Kompilieren Sie mit einer Version von g ++ unterstützt C++ 11 (in meinem Fall gcc 4.7.2).

[Prompt] g++ -oparseline parseline.cpp -std=c++11 -O2 
[Prompt] ./parseline players.txt 
Name: Fernando Torres, Number: 9, Points: -29 
Name: Cristiano Ronaldo, Number: 7, Points: 31 
Name: Wayne Rooney, Number: 10, Points: 37 
Name: Neymar, Number: 17, Points: 29 
Name: Andres Iniesta, Number: 8, Points: 32 
Name: Robin van Persie, Number: 19, Points: 20 
Name: Lionel Messi, Number: 10, Points: 43 
Name: Xavi Hernandez, Number: 6, Points: 36 
Name: Mesut Özil, Number: 10, Points: 38 
Name: Didier Drogba, Number: 10, Points: 35 
Name: Fernando Torres, Number: 9, Points: 29 
Name: Kaká, Number: 10, Points: 17 
Verwandte Themen