W
Hallo Mandel,
ich habe zwei Helferlein gebastelt. Zum einen 'tab', was ein Tabulator-Zeichen einliest und die Abfrage 'is_number', welche true liefert, wenn ein Zeichen folgt, dass eine Zahl einleiten könnte. Damit kann man dann relativ einfach die von Dir beschriebene Datei einlesen (s. main).
Damit das funktioniert, muss vor jeder potentiellen Zahl (auch der in der ersten Spalte) ein TAB stehen - unabhängig davon, ob danach eine Zahl kommt oder nicht. Weiter gehe ich davon aus, dass die Anzahl der Spalten vor dem Einlesen bekannt ist (im Programm unten == 5).
Hier der gesamte Code
#include <iostream>
#include <fstream>
#include <limits> // numeric_limits
// -- liest das nächste Tabulator-Zeichen ein
// falls keines gefunden wurde, oder Text folgt, so wird der Stream auf FAIL gesetzt
template< typename E, typename Traits >
std::basic_istream< E, Traits >& tab( std::basic_istream< E, Traits >& in )
{
std::basic_istream< E, Traits >::sentry ok( in, true ); // true := noskip white space
if( ok )
{
const std::ctype< E >& ctype_ = std::use_facet< std::ctype< E > >( in.getloc() );
for( std::basic_istream< E, Traits >::int_type m = in.rdbuf()->sgetc(); ; m = in.rdbuf()->snextc() )
{
if( Traits::eq_int_type( m, Traits::eof() ) )
{
in.setstate( std::ios_base::eofbit | std::ios_base::failbit );
break; // EOF ist hier auch Fehler, da ein Tab gelesen werden soll
}
const E c = Traits::to_char_type( m );
if( c == in.widen( '\t' ) )
{
in.rdbuf()->sbumpc(); // Tabulator konsumieren
break; // ok, Tab gelesen
}
if( !ctype_.is( std::ctype_base::space, c ) )
{
in.setstate( std::ios_base::failbit );
break; // .. es folgt ein Text -> das ist wäre hier ein Fehler - oder?!
}
}
}
return in;
}
// -- liefert true, wenn im unmittelbar folgenden Zeichen des Streams 'in'
// ein Zeichen folgt, welches zu einem numerischen Ausdruck gehören könnte.
// e := { Digit, Vorzeichen, Dezimalpunkt }
// Es wird kein Zeichen konsumiert (d.h. eingelesen).
// Ist das aktuelle Zeichen das letzte im Stream, so wird EOF gesetzt.
// Ist EOF bereits gesetzt, so geht der Stream auf FAIL (s. istream::sentry).
template< typename E, typename Traits >
bool is_number( std::basic_istream< E, Traits >& in )
{
std::basic_istream< E, Traits >::sentry ok( in, true ); // true := noskip white space
if( ok )
{
const std::basic_istream< E, Traits >::int_type m = in.rdbuf()->sgetc();
if( Traits::eq_int_type( m, Traits::eof() ) )
{
in.setstate( std::ios_base::eofbit );
return false;
}
const E c = Traits::to_char_type( m );
return std::use_facet< std::ctype< E > >( in.getloc() ).is( std::ctype_base::digit, c )
|| c == in.widen('-') || c == in.widen('+')
|| c == std::use_facet< std::numpunct< E > >( in.getloc() ).decimal_point();
}
return false;
}
namespace
{
const std::streamsize ALL = std::numeric_limits< std::streamsize >::max();
}
int main()
{
using namespace std;
ifstream File("Datei.txt");
if( !File.is_open() )
{
cerr << "Fehler beim Oeffnen der Datei" << endl;
return -1;
}
// -- Header überlesen
const int anzahlZeilenImHeader = 6;
for( int i=0; i < anzahlZeilenImHeader; ++i )
File.ignore( ALL, '\n' ); // Zeile bis LF überlesen
// -- Tabelle einlesen; bis Fehler oder Ende
for( int zeile = 0; File; ++zeile )
{
for( int spalte = 0; spalte < 5; ++spalte ) // mal angenommen es sind 5 Spalten!
{
double num = 0.0; // Defaultwert, falls keine Zahl in der Datei steht
if( File >> tab && (!is_number( File ) || File >> num) )
{
cout << '\t' << num; // hier ist 'num' in [zeile,spalte] gültig
}
else
break; // Lesefehler bzw. EOF
}
cout << endl;
}
if( !File.eof() )
cerr << "beim Lesen ist ein Fehler aufgetreten" << endl;
return 0;
}
Gruß
Werner