Config Datei lesen/schreiben



  • Hallo!

    Habe auf http://www-personal.umich.edu/~wagnerr/ConfigFile.html ein Klasse gefunden die Konfigurationsdateien ließt, verändern und schreiben soll.

    ConfigFile.cpp:

    #include "ConfigFile.h"
    
    using std::string;
    
    ConfigFile::ConfigFile( string filename, string delimiter,
                            string comment, string sentry )
    	: myDelimiter(delimiter), myComment(comment), mySentry(sentry)
    {
    	// Construct a ConfigFile, getting keys and values from given file
    
    	std::ifstream in( filename.c_str() );
    
    	if( !in ) throw file_not_found( filename ); 
    
    	in >> (*this);
    }
    
    ConfigFile::ConfigFile()
    	: myDelimiter( string(1,'=') ), myComment( string(1,'#') )
    {
    	// Construct a ConfigFile without a file; empty
    }
    
    void ConfigFile::remove( const string& key )
    {
    	// Remove key and its value
    	myContents.erase( myContents.find( key ) );
    	return;
    }
    
    bool ConfigFile::keyExists( const string& key ) const
    {
    	// Indicate whether key is found
    	mapci p = myContents.find( key );
    	return ( p != myContents.end() );
    }
    
    /* static */
    void ConfigFile::trim( string& s )
    {
    	// Remove leading and trailing whitespace
    	static const char whitespace[] = " \n\t\v\r\f";
    	s.erase( 0, s.find_first_not_of(whitespace) );
    	s.erase( s.find_last_not_of(whitespace) + 1U );
    }
    
    std::ostream& operator<<( std::ostream& os, const ConfigFile& cf )
    {
    	// Save a ConfigFile to os
    	for( ConfigFile::mapci p = cf.myContents.begin();
    	     p != cf.myContents.end();
    		 ++p )
    	{
    		os << p->first << " " << cf.myDelimiter << " ";
    		os << p->second << std::endl;
    	}
    	return os;
    }
    
    std::istream& operator>>( std::istream& is, ConfigFile& cf )
    {
    	// Load a ConfigFile from is
    	// Read in keys and values, keeping internal whitespace
    	typedef string::size_type pos;
    	const string& delim  = cf.myDelimiter;  // separator
    	const string& comm   = cf.myComment;    // comment
    	const string& sentry = cf.mySentry;     // end of file sentry
    	const pos skip = delim.length();        // length of separator
    
    	string nextline = "";  // might need to read ahead to see where value ends
    
    	while( is || nextline.length() > 0 )
    	{
    		// Read an entire line at a time
    		string line;
    		if( nextline.length() > 0 )
    		{
    			line = nextline;  // we read ahead; use it now
    			nextline = "";
    		}
    		else
    		{
    			std::getline( is, line );
    		}
    
    		// Ignore comments
    		line = line.substr( 0, line.find(comm) );
    
    		// Check for end of file sentry
    		if( sentry != "" && line.find(sentry) != string::npos ) return is;
    
    		// Parse the line if it contains a delimiter
    		pos delimPos = line.find( delim );
    		if( delimPos < string::npos )
    		{
    			// Extract the key
    			string key = line.substr( 0, delimPos );
    			line.replace( 0, delimPos+skip, "" );
    
    			// See if value continues on the next line
    			// Stop at blank line, next line with a key, end of stream,
    			// or end of file sentry
    			bool terminate = false;
    			while( !terminate && is )
    			{
    				std::getline( is, nextline );
    				terminate = true;
    
    				string nlcopy = nextline;
    				ConfigFile::trim(nlcopy);
    				if( nlcopy == "" ) continue;
    
    				nextline = nextline.substr( 0, nextline.find(comm) );
    				if( nextline.find(delim) != string::npos )
    					continue;
    				if( sentry != "" && nextline.find(sentry) != string::npos )
    					continue;
    
    				nlcopy = nextline;
    				ConfigFile::trim(nlcopy);
    				if( nlcopy != "" ) line += "\n";
    				line += nextline;
    				terminate = false;
    			}
    
    			// Store key and value
    			ConfigFile::trim(key);
    			ConfigFile::trim(line);
    			cf.myContents[key] = line;  // overwrites if key is repeated
    		}
    	}
    
    	return is;
    }
    

    ConfigFile.h

    // Typical usage
    // -------------
    // 
    // Given a configuration file "settings.inp":
    //   atoms  = 25
    //   length = 8.0  # nanometers
    //   name = Reece Surcher
    // 
    // Named values are read in various ways, with or without default values:
    //   ConfigFile config( "settings.inp" );
    //   int atoms = config.read<int>( "atoms" );
    //   double length = config.read( "length", 10.0 );
    //   string author, title;
    //   config.readInto( author, "name" );
    //   config.readInto( title, "title", string("Untitled") );
    // 
    // See file example.cpp for more examples.
    
    #ifndef CONFIGFILE_H
    #define CONFIGFILE_H
    
    #include <string>
    #include <map>
    #include <iostream>
    #include <fstream>
    #include <sstream>
    
    using std::string;
    
    class ConfigFile {
    // Data
    protected:
    	string myDelimiter;  // separator between key and value
    	string myComment;    // separator between value and comments
    	string mySentry;     // optional string to signal end of file
    	std::map<string,string> myContents;  // extracted keys and values
    
    	typedef std::map<string,string>::iterator mapi;
    	typedef std::map<string,string>::const_iterator mapci;
    
    // Methods
    public:
    	ConfigFile( string filename,
    	            string delimiter = "=",
    	            string comment = "#",
    				string sentry = "EndConfigFile" );
    	ConfigFile();
    
    	// Search for key and read value or optional default value
    	template<class T> T read( const string& key ) const;  // call as read<T>
    	template<class T> T read( const string& key, const T& value ) const;
    	template<class T> bool readInto( T& var, const string& key ) const;
    	template<class T>
    	bool readInto( T& var, const string& key, const T& value ) const;
    
    	// Modify keys and values
    	template<class T> void add( string key, const T& value );
    	void remove( const string& key );
    
    	// Check whether key exists in configuration
    	bool keyExists( const string& key ) const;
    
    	// Check or change configuration syntax
    	string getDelimiter() const { return myDelimiter; }
    	string getComment() const { return myComment; }
    	string getSentry() const { return mySentry; }
    	string setDelimiter( const string& s )
    		{ string old = myDelimiter;  myDelimiter = s;  return old; }  
    	string setComment( const string& s )
    		{ string old = myComment;  myComment = s;  return old; }
    
    	// Write or read configuration
    	friend std::ostream& operator<<( std::ostream& os, const ConfigFile& cf );
    	friend std::istream& operator>>( std::istream& is, ConfigFile& cf );
    
    protected:
    	template<class T> static string T_as_string( const T& t );
    	template<class T> static T string_as_T( const string& s );
    	static void trim( string& s );
    
    // Exception types
    public:
    	struct file_not_found {
    		string filename;
    		file_not_found( const string& filename_ = string() )
    			: filename(filename_) {} };
    	struct key_not_found {  // thrown only by T read(key) variant of read()
    		string key;
    		key_not_found( const string& key_ = string() )
    			: key(key_) {} };
    };
    
    /* static */
    template<class T>
    string ConfigFile::T_as_string( const T& t )
    {
    	// Convert from a T to a string
    	// Type T must support << operator
    	std::ostringstream ost;
    	ost << t;
    	return ost.str();
    }
    
    /* static */
    template<class T>
    T ConfigFile::string_as_T( const string& s )
    {
    	// Convert from a string to a T
    	// Type T must support >> operator
    	T t;
    	std::istringstream ist(s);
    	ist >> t;
    	return t;
    }
    
    /* static */
    template<>
    inline string ConfigFile::string_as_T<string>( const string& s )
    {
    	// Convert from a string to a string
    	// In other words, do nothing
    	return s;
    }
    
    /* static */
    template<>
    inline bool ConfigFile::string_as_T<bool>( const string& s )
    {
    	// Convert from a string to a bool
    	// Interpret "false", "F", "no", "n", "0" as false
    	// Interpret "true", "T", "yes", "y", "1", "-1", or anything else as true
    	bool b = true;
    	string sup = s;
    	for( string::iterator p = sup.begin(); p != sup.end(); ++p )
    		*p = toupper(*p);  // make string all caps
    	if( sup==string("FALSE") || sup==string("F") ||
    	    sup==string("NO") || sup==string("N") ||
    	    sup==string("0") || sup==string("NONE") )
    		b = false;
    	return b;
    }
    
    template<class T>
    T ConfigFile::read( const string& key ) const
    {
    	// Read the value corresponding to key
    	mapci p = myContents.find(key);
    	if( p == myContents.end() ) throw key_not_found(key);
    	return string_as_T<T>( p->second );
    }
    
    template<class T>
    T ConfigFile::read( const string& key, const T& value ) const
    {
    	// Return the value corresponding to key or given default value
    	// if key is not found
    	mapci p = myContents.find(key);
    	if( p == myContents.end() ) return value;
    	return string_as_T<T>( p->second );
    }
    
    template<class T>
    bool ConfigFile::readInto( T& var, const string& key ) const
    {
    	// Get the value corresponding to key and store in var
    	// Return true if key is found
    	// Otherwise leave var untouched
    	mapci p = myContents.find(key);
    	bool found = ( p != myContents.end() );
    	if( found ) var = string_as_T<T>( p->second );
    	return found;
    }
    
    template<class T>
    bool ConfigFile::readInto( T& var, const string& key, const T& value ) const
    {
    	// Get the value corresponding to key and store in var
    	// Return true if key is found
    	// Otherwise set var to given default
    	mapci p = myContents.find(key);
    	bool found = ( p != myContents.end() );
    	if( found )
    		var = string_as_T<T>( p->second );
    	else
    		var = value;
    	return found;
    }
    
    template<class T>
    void ConfigFile::add( string key, const T& value )
    {
    	// Add a key with given value
    	string v = T_as_string( value );
    	trim(key);
    	trim(v);
    	myContents[key] = v;
    	return;
    }
    
    #endif  // CONFIGFILE_H
    

    triplet.h

    #include <iostream>
    
    struct Triplet
    {
    	int a, b, c;
    
    	Triplet() {}
    	Triplet( int u, int v, int w ) : a(u), b(v), c(w) {}
    	Triplet( const Triplet& orig ) : a(orig.a), b(orig.b), c(orig.c) {}
    
    	Triplet& operator=( const Triplet& orig )
    		{ a = orig.a;  b = orig.b;  c = orig.c;  return *this; }
    };
    
    std::ostream& operator<<( std::ostream& os, const Triplet& t )
    {
    	// Save a triplet to os
    	os << t.a << " " << t.b << " " << t.c;
    	return os;
    }
    
    std::istream& operator>>( std::istream& is, Triplet& t )
    {
    	// Load a triplet from is
    	is >> t.a >> t.b >> t.c;
    	return is;
    }
    

    Der Autor hat leider kein Beispiel zur Sicherung der veränderten Datei.

    Habe dann mit Hilfe von dieser Seite:

    ofstream file;
    file.open ("test.conf");
    file << config;
    

    , diesen Code zum speichern verwendet.

    Mein Problem ist jetzt das in der ursprünglichen Konfigurationsdatei noch Kommentare stehen (#test), die jetzt beim auslesen und überschreiben der Datei verloren gehen. Die Kommentare sollen aber erhalten bleiben, weiß aber nicht wie ich den Code so verändern kann.

    Kann mir jemand dabei helfen?

    Danke und Gruß
    nihilfire



  • Wenn eine Config-Date veraendert wird, dann stimmen meist die Kommentare nicht mehr. Warum sollte man die Kommentare also beibehalten?



  • nihilfire schrieb:

    Mein Problem ist jetzt das in der ursprünglichen Konfigurationsdatei noch Kommentare stehen (#test), die jetzt beim auslesen und überschreiben der Datei verloren gehen. Die Kommentare sollen aber erhalten bleiben, weiß aber nicht wie ich den Code so verändern kann.

    Das ist in diesem Code gar nicht vorgesehen.
    Und die Kommentare ordentlich und konsistent zu den Werten zu erhalten, ist auch keine einfache Sache. Und auch gar nicht allgemein lösbar. Ich kann nur davon abraten.


Anmelden zum Antworten