# Textdatei einlesen und in Struktur ablegen, fgets fscanf oder strtok?



## tromtom (12. April 2008)

Hallo

Ich möchte eine Textdatei mit z.B. folgendem Inhalt einlesen:
1 | Bjarne Stroustrup | Die C++-Programmiersprache | Addison-Wesley | 2000 | c++
2 | George Orwell | 1984|Ullstein | 2005 | 1984
3 | Frank Geisler | Datenbanken Grundlagen | Mitp-Verlag | 2005 | datenbanken
....usw

Dazu verwende ich 2 Strukturen:

struct Buch                                  
{                                                 
    char Autor[21];                             
    char Titel[41];                              
    char Verlagsname[21];             
    int Erscheinungsjahr;
};

struct Index
{
    char Ordnungsbegriff[41];
    int Position;
};

Ich komme bei der einlesen-Funktion nicht weiter:


```
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 100
#define EINGABE "buecher.txt"

//***************** Strukturen Definition ************************************//
struct Buch {
       char Autor[21];
       char Titel[41];
       char Verlag[21];
       int Erscheinungsjahr;
       };
             
struct Index {
       char Ordnungsbegriff[41];
       int Position;
       };
     
void einlesen();
void ausgeben();
void sortieren();  
void suchen();
       
int main(int argc, char **argv) {
    FILE *in, *out;
    struct Buch s_Buch[N];
    struct Index s_Index[N];
    char auswahl;
    
    do{
    printf("\n ---- Willkommen --- \n");
    printf("\n I - Daten importieren");
    printf("\n A - Daten ausgeben");
    printf("\n F - bestimmten Datensatz ausgeben");
    printf("\n S - Daten sortieren");
    printf("\n X - Beenden");
    printf("\n Bitte auswaehlen: ");
    scanf("%c",&auswahl);
    
    switch(auswahl)
    {
       case 'i':
       case 'I':   
                   einlesen(s_Index, s_Buch, in);
       break;
       case 'a':
       case 'A':   
                   ausgeben();
       break;
       case 'f':
       case 'F':   
                   suchen();
       break;
       case 's':
       case 'S':   
                   sortieren();
       break;
       case 'x':
       case 'X':   
                   exit(0);
       break;
       }
    }
    while(auswahl != 'x' || auswahl != 'X');
    return 0;
}

void einlesen(struct Index *a_index, struct Buch *a_buch,  FILE *in)
{
    in=fopen(EINGABE, "r");
    if(in==NULL)
    {
                printf("Fehler beim Oeffnen ...\n");
                getchar(); 
    }
    else
    printf("\n Eingabedatei %s erfolgreich geoeffnet! \n", EINGABE);
    
    /******** Variante 1: Formartiertes einlesen mit fscanf ********/

    int i=0;
   while (!feof(in))
    {
          fscanf(in, "%s | %s | %s | %s | %s | %s ", &a_index[i].Position, &a_buch[i].Autor, &a_buch[i].Titel, &a_buch[i].Verlag, &a_buch[i]Erscheinungsjahr, &a_index[i].Ordnungsbegriff);
          i++; 
     }  

    /******** Variante 2 ********/
    /* Zeilenweise einlesen und mit strtok den String nach den Trennzeichen aufteilen */
    char buf[100];
    while( fgets(buf,sizeof(buf),in) != NULL)
	{
		a_index[i].Position = atoi(strtok(buf,"|"));
		strcpy(a_buch[i].Autor, strtok(NULL,"|"));
		strcpy(a_buch[i].Titel, strtok(NULL,"|"));
		strcpy(a_buch[i].Verlag,strtok(NULL,"|"));
		a_buch[i].Erscheinungsjahr=atoi(strtok(NULL,"|"));
		strcpy(a_index[i].Ordnungsbegriff, strtok(NULL,"|"));
		++i;
	}                              
    printf("\n ...erfolgreich importiert! \n");
    getchar();
}

void ausgeben(struct Index *a_index, struct Buch *a_buch)
{
     int i;
     for(i=0; i<5; i++)
     {
              printf("\n\n Position: ", &a_index[i].Position);
              printf("\n Autor: ", &a_buch[i].Autor);
              printf("\n Titel: ", &a_buch[i].Titel);
              printf("\n Verlag: ", &a_buch[i].Verlag);
              printf("\n Erscheinungsjahr: ", &a_buch[i].Erscheinungsjahr);
              printf("\n Ordnungsbegriff: ", &a_index[i].Ordnungsbegriff);
     }
     getchar();
}
```

Das Beispiel von GalileoComputing ist gut aber es bringt mich nicht weiter da die Logfile hier quasi während des einlesens sofort in der Schleife ausgegeben wird:


```
while((fscanf(CSV,"%d:%d,%d.%d.%d,%d:%d,%d.%d.%d,%s\n",
      &login_hour,&login_min,&date_day,&date_mon,&date_year,
      &logout_hour,&logout_min,&date_dayx,&date_monx,
      &date_yearx,name)) != EOF )

       fprintf(stdout,"User:%s\nLogin um:%d:%d Uhr am %d.%d.%d\n"
                      "Logout um : %d:%d Uhr am %d.%d.%d\n\n",
         name,login_hour,login_min,date_day,date_mon,date_year,
         logout_hour,logout_min,date_dayx,date_monx, date_yearx);
```

Ich hoffe Ihr könnt mir weiterhelfen. Ich bin für jeden Tipp dankbar.
Gruss
tromtom


----------



## devDevil (12. April 2008)

Sicher das du C willst? Mit C++ wäre das schön zu lösen ... ^^


----------



## tromtom (12. April 2008)

Kann auch C++ sein.
Wobei ich mich mit den C++ Stringklassen nicht so gut auskenne...


----------



## devDevil (13. April 2008)

*Header "Book.hpp"*

```
#if !defined (BOOK_HPP__INCLUDED)
#define BOOK_HPP__INCLUDED

#if (_MSC_VER >= 1300)
#pragma once
#endif // (_MSC_VER >= 1300)

#include <iostream>
#include <string>
#include <cctype>
#include <algorithm>

struct Book                                  
{                                                 
    unsigned int id;
    std::string author;
    std::string title;
    std::string publisher;
    unsigned short year;
    std::string category;

public:
    friend std::istream& operator>>(std::istream& in, Book& data)
    {
        std::getline((in >> data.id).ignore(), data.author, '|');
        std::getline(in, data.title, '|');
        std::getline(in, data.publisher, '|');
        std::getline((in >> data.year).ignore(), data.category);
        std::transform(data.category.begin() + 1, data.category.end(), data.category.begin() + 1, std::tolower);
    }

    friend std::ostream& operator<<(std::ostream& out, Book const& data)
    { return out << "\"" << data.title << "\" von " << data.author << "\nVerlag: " << data.publisher << "\nErscheinungsjahr: " << data.year << "\nPosition: " << data.id << data.category.empty() ? "" : "\nKategorie: " << data.category; }

public:
    const bool operator<(Book const& rhs) const 
    { return id < rhs.id; }
    const bool operator>(Book const& rhs) const 
    { return id > rhs.id; }
	const bool operator==(Book const& rhs) const
	{ return this == &rhs ? true : id == rhs.id; }

    const bool in_category(std::string const& data) const 
    { return category == data; } 
};

#endif // BOOK_HPP__INCLUDED
```

*Header Utility.hpp*

```
#if !defined(UTILITY_HPP__INCLUDED)
#define UTILITY_HPP__INCLUDED

#if (_MSC_VER >= 1300)
#pragma once
#endif // (_MSC_VER >= 1300)

#include <iostream>
#include <string>
#include <limits>

namespace utility
{
	template<typename input_type>
	std::istream& type(std::istream& in, std::string const& prompt, input_type& input) 
	{ 
		while (std::cout << prompt << std::flush && !in >> input) 
		{ 
			std::cout << "Falsche Eingabe!" << std::endl; 
			in.clear(); in.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); 
		}
		return in;
	}
}

#endif // UTILITY_HPP__INCLUDED
```

Und das ganze findet dann Anwendung in:
*Quelldatei main.cpp*

```
#include "utility.hpp"
#include "book.hpp"
#include <vector>
#include <iostream>
#include <fstream>
#include <algorithm>
#include <iterator>
#include <cctype>

int main()
{
	std::vector<Book> library;
	char selection;
	do {
		std::cout	<< " --- Willkommen --- \n\n"
					<< "I - Daten importieren\n"
					<< "A - Daten ausgeben\n"
					<< "F - Einen bestimmten Datensatz ausgeben\n"
					<< "S - Daten sortieren\n"
					<< "X - Beenden" << std::endl;

                utility::type(std::cin, "Eingabe: ", selection);
		selection = std::tolower(selection);

		switch (selection)
		{
			case 'i':
			{
				std::ifstream file_stream("buecher.txt");
				std::copy(std::istream_iterator<Book>(file_stream), std::istream_iterator<Book>(), std::back_inserter(library));
			} break;
			case 'a':
			{
				std::copy(library.begin(), library.end(), std::ostream_iterator<char>(std::cout, "\n"));
			} break;
			case 'f':
			{
				unsigned int id;
				utility::type(std::cin, "Buch-Position: ", id);
				std::vector<Book>::cosnt_iterator it = std::find(library.begin(), library.end(), id);
				
				if (it == library.end()) 
					std::cout <<  "Buch nicht vorhanden!" << std::endl;
				else
					std::cout << *it << std::endl;
			} break;
			case 's':
			{
				std::sort(library.begin(), library.end());
			} break;
                        default:
                        {
                                std::cout << "Diesem Buchstaben ist kein Menüpunkt zugewiesen!" << std::endl;
                        } break;
		}
	}
    while (selection != 'x');
}
```

Hmm hab beim letzen mal als ich hier jemanden was geschrieben hab gehört das Fehler im Code waren. Das KANN SEIN! Ich hab den Code hier nur ins Fenster getippt. Wenn Fehler auftreten sag mir nochmal bescheid. 

Ansonsten sollte alles fertig implementiert sein, was du haben wolltest


----------



## tromtom (13. April 2008)

Vielen dank erstmal.
Die einlesen-Funktion hätte schon gereicht, danke für die Mühe.
Hab das gerade kompiliert und er hat mir einige Fehlermeldungen ausgespuckt aber ich hab ja jetzt Zeit um sie mir mal genauer anzuschauen. 
Werd mir jetzt erstmal die Dokus zu den Klassen der String-Streams ansehen um das ganze nachvollziehen zu können.

gruss
tromtom


----------



## devDevil (13. April 2008)

Jo versuch es nachzuvollziehen und sonst frag einfach nach. Helfe gerne!


----------

