C# wie Variablen dauerhaft speichern

Thaxll'ssillyia

Captain
Registriert
Dez. 2007
Beiträge
3.591
Hallo an alle C#-Fans!

Ich möchte für mich in C# ein Adressbuch schreiben.
Dabei möchte ich bestimmte Variablen(string, int) sowie Arrays und Strukturen dauerhaft in einer neben der .exe liegenden Datei speichern und dann bestimmte Werte an das Programm übergeben, Werte ändern oder löschen.

Eine Textdatei geht nicht, weil man die Information einer bestimmten Zeile nicht einfach herauslesen kann.
Da dacht ich mir, kann ich die Variablen nicht in einer .exe-Ähnlichen Datei (.dll?) ablegen und dann via Adressbuch String1 = Datei.String1 oder so ähnlich zugreifen?

Vielen Dank für Hilfe!

Gruß Thax
 
Du kannst eine Datenbank nutzen wenn es viele Datensätze werden sollen oder du nutzt den XML Serialisierung. Damit geht es einfach und schnell und bei einem normalen Adressbuch sollte es keine Probleme mit der Performance geben.
 
ähnlich wie DLL? sowas ähnliches gibt es ja garnicht! du meinst vielleicht dein adressbuch zusätzlich verschlüsseln oder?
 
oder du nutzt den XML Serialisierung

Kann ich das von jedem x-belibigen Rechner aufrufen oder ist es eine Erweiterung, die erst installiert werden muss?
ähnlich wie DLL? sowas ähnliches gibt es ja garnicht!

Text nochmal lesen (exe-ähnlicher-Datei)

du meinst vielleicht dein adressbuch zusätzlich verschlüsseln oder?

Naja, an sich wär es gut, wenn man die Daten in der Datei nicht so leicht einsehen/abändern könnte. Allerdings wär es nicht schlimm, wenn es doch geht, übers Prog ist es eh einfacher.

Ich bin erstmal froh, wenn ich die Daten in irgendeine Datei übergeben kann, wo ich sie entweder Zeilenweise auslesen kann oder einfach über so etwas wie Datei.Variable zugreifen kann.
 
Zuletzt bearbeitet:
naja die Datei1.Variable muss auch irgendwo gespeichert werden.

das was am nähesten an deinen Vorschlag hinkommt ist wiegesagt eine XML Datei, da kannst du recht einfach daten speichern (is eig. im endeffekt wie ne tabelle)

soweit ich weiß definierst du dabei welche spalten es gibt, und kannst diese dann beliebig auslesen
 
was stellst du dir unter "exe" Datei vor? Das ist eine Binary, du kannst die ja auch nicht so ohne lesen. Du meinst mit doppelklick wird diese Adressbuchdatei dann aufgerufen? Also an sich kannst du keine Binary oder DLL nach dem kompilieren schreiben. Du kannst ledeglich neukompilieren was du aber nicht willst. Benutze doch XMLWriter /-Reader in C# oder mache es mit Datenbank wenn du irgendwas verschlüsseln willst, da du ehe einen Schlüssel (PW) für den Zugriff auf die DB brauchst.
 
was stellst du dir unter "exe" Datei vor? Das ist eine Binary, du kannst die ja auch nicht so ohne lesen. Du meinst mit doppelklick wird diese Adressbuchdatei dann aufgerufen?

Ich meinte so etwas wie eine dauerhafte Exe, oder auch so etwas wie eine Textdatei, auf die ich einen bestimmten Inhalt an einer bestimmten Stelle herauslesen kann.

Verschlüsseln ist mir erst mal nicht wichtig.
 
Ja und einfacher gehts dank dem XmlSerializer wirklich nicht.

Code:
cclass Program
    {
        static void Main(string[] args)
        {
            List<AddressData> addressBook = new List<AddressData>();

            addressBook.Add(new AddressData("Max", "Mustermann", 33, "m.mustermann@web.de"));
            addressBook.Add(new AddressData("Peter", "Enis", 44, "p.enis@web.de"));

            foreach (AddressData aData in addressBook)
                Console.Write(aData.ToString() + "\n");


            //Daten in XML Datei serialisieren (Daten speichern)
            SerializeAddressBook(addressBook);

            Console.Write("\n");


            //XML Datei deserialisieren (Daten laden)
            List<AddressData> anotherAddressBook = DeserializeAddressBook();

            foreach (AddressData aData in anotherAddressBook)
                Console.Write(aData.ToString() + "\n");

            Console.ReadKey();
        }

        public static List<AddressData> DeserializeAddressBook()
        {
            //der Typ der zu deserialisierenden Klasse muss übergeben werden            
            XmlSerializer deserializer = new XmlSerializer(typeof(List<AddressData>));
            
            FileStream fs = new FileStream("addressbook.xml", FileMode.Open);
            List<AddressData> addressBook = (List<AddressData>)deserializer.Deserialize(fs);
            
            fs.Close();

            return addressBook;
        }

        public static void SerializeAddressBook(List<AddressData> addressBook)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(List<AddressData>));
            FileStream fs = new FileStream("addressbook.xml", FileMode.Create);

            serializer.Serialize(fs, addressBook);
            
            fs.Close();
        }
    }    

    public class AddressData
    {
        public string FirstName
        { get; set; }

        public string LastName
        { get; set; }

        public int Age
        { get; set; }

        public string MailAddress
        { get; set; }

        //usw.

        //Der XmlSerializer benötigt einen leeren Konstruktor 
        //um eine Klasse serialisieren zu können
        public AddressData()
        { }

        public AddressData(string firstName, string lastName, int age, string mailAddress)
        {
            FirstName = firstName;
            LastName = lastName;
            Age = age;
            MailAddress = mailAddress;
        }

        public override string ToString()
        {
            return String.Format("Name: {0} {1}\nAlter: {2}\neMe-mail: {3}", FirstName, LastName, Age, MailAddress);
        }
    }
 
Hey, vielen Dank für den Beispielcode.
Ich werd den jetzt mal Stück für Stück auseinander nehmen und versuchen zu kapieren, was drinsteht:)

Hätt nicht gedacht, dass das so kompliziert ist, dachte, es geht einfach mit

- Übergeben einer Programm-Var an XML-Variable
- Übergeben einer XML-Var an eine Programm-Var

Gruß Thax
 
Zuletzt bearbeitet:
Natürlich kann man auch eine DLL erzeugen, in der die Daten vorliegen, das Laden könnte sogar performanter sein als das Parsen oder Serializieren:
Hier die Verwendung von der AdressenListe-Klasse:
Code:
      AdressenListe adressenListe = new AdressenListe();
      adressenListe.Add(new Adresse("Rossibaer", "Dankward", "Sonnenalle 9", "64325", "Mäuselwitz"));
      adressenListe.Add(new Adresse("Musterfrau", "Hannah", "Am Tor 1", "99999", "Mauerbach" ));
      adressenListe.Add(new Adresse("Mustermann", "Thomas", "Hinterm Bach 3", "64131", "Musterhausen"));
      adressenListe.Save();
      adressenListe.Clear();
      adressenListe.Load();
      foreach (Adresse address in adressenListe)
      {
        Console.WriteLine();
        Console.WriteLine(address);
      }
      Console.ReadKey();
Nun die AdressenListe Klasse selbst:
Code:
  public class AdressenListe : List<Adresse>
  {
    public const string DatenbankName = "Datenbank";
    public const string DatenbankFileName = DatenbankName + ".dll";
    public const string AddressenLoader = "AddressenLoader";
    public void Save()
    {
      // Zuerst einmal eine Kompiliereinheit erstellen ...
      System.CodeDom.CodeCompileUnit unitDatabase = new System.CodeDom.CodeCompileUnit();
      // Namespace ist gut und wichtig (keine Klasse ohne Namespace im .Net Framework!) ...
      System.CodeDom.CodeNamespace ns = new System.CodeDom.CodeNamespace(DatenbankName);
      unitDatabase.Namespaces.Add(ns);
      // Nun die Klasse "AddressenLoader" erzeugen ...
      System.CodeDom.CodeTypeDeclaration typeRawData = new System.CodeDom.CodeTypeDeclaration(AddressenLoader);
      typeRawData.Attributes = System.CodeDom.MemberAttributes.Public;
      ns.Types.Add(typeRawData);
      // Der Konstruktor wird erstellt ...
      System.CodeDom.CodeConstructor ctorAddressList = new System.CodeDom.CodeConstructor();
      typeRawData.Members.Add(ctorAddressList);
      ctorAddressList.Attributes = System.CodeDom.MemberAttributes.Public;
      System.CodeDom.CodeParameterDeclarationExpression paramList = new System.CodeDom.CodeParameterDeclarationExpression(typeof(AdressenListe), "list");
      ctorAddressList.Parameters.Add(paramList);
      System.CodeDom.CodeVariableReferenceExpression refList = new System.CodeDom.CodeVariableReferenceExpression(paramList.Name);
      // Exportiere nun alle Adressen ...
      foreach (Adresse address in this)
      {
        System.CodeDom.CodeArrayCreateExpression newAddressFields = new System.CodeDom.CodeArrayCreateExpression(typeof(string[]));
        foreach (string field in address.Daten)
          newAddressFields.Initializers.Add(new System.CodeDom.CodePrimitiveExpression(field));
        System.CodeDom.CodeObjectCreateExpression newAddress = new System.CodeDom.CodeObjectCreateExpression(typeof(Adresse));
        newAddress.Parameters.Add(newAddressFields);
        System.CodeDom.CodeMethodInvokeExpression refAdd = new System.CodeDom.CodeMethodInvokeExpression(refList, "Add");
        refAdd.Parameters.Add(newAddress);
        ctorAddressList.Statements.Add(refAdd);
      }
      // jetzt noch ein bißchen den Compiler und paar Optionen eingestellt ...
      System.CodeDom.Compiler.CodeDomProvider provider = System.CodeDom.Compiler.CodeDomProvider.CreateProvider("cs");
      string sOutputFile = System.IO.Path.Combine(System.IO.Path.GetTempPath(), DatenbankFileName);
      System.IO.FileInfo tempDatenbank = new System.IO.FileInfo(sOutputFile);
      System.CodeDom.Compiler.CompilerParameters paramsAddress = new System.CodeDom.Compiler.CompilerParameters(
        new string[] { "mscorlib.dll", "System.dll", this.GetType().Assembly.Location }, tempDatenbank.FullName, false);
      paramsAddress.GenerateExecutable = false;
      // fertig! also nix wie kompiliert ...
      System.CodeDom.Compiler.CompilerResults results = provider.CompileAssemblyFromDom(paramsAddress, unitDatabase);
      // ein bisschen Aufräumen kann nicht schaden ...
      provider.Dispose();
      // noch mal schnell nach möglichen Kompilierfehler geschaut ...
      if (results.Errors.Count != 0)
        throw new Exception(string.Concat("Daten konnten nicht gespeichert werden. Interner Kompilierfehler.\r\n", 
          results.Errors[0].ToString()));
      // Kompiliert wurde im Temp Verzeichnis, bevor die DLL dann ins Zielverzeichnis verschoben wird
      // schauen ob die Größe nicht das Maximum überschreitet (siehe das notwendige Limit in
      // der Load() Methode
      tempDatenbank.Refresh();
      if (tempDatenbank.Length > (long)int.MaxValue)
        throw new Exception("Datenbank hat das Maximum (2GB) der Größe überschritten.");
      // nun ist alles ok, also die Datenbank.dll ins Zielverzeichnis verschieben
      System.IO.FileInfo fileDatenbank = new System.IO.FileInfo(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DatenbankFileName));
      if (fileDatenbank.Exists)
        fileDatenbank.Delete();
      tempDatenbank.MoveTo(fileDatenbank.FullName);
    }
    public void Load()
    {
      this.Clear();
      // Das dynamische Laden einer .Net Framework DLL geht mit etwas weniger Aufwand als das Kompilieren
      // jedoch kann man die DLL nicht direkt im Projekt als Verweis hinzufügen, da während der Laufzeit die DLL für
      // Schreibzugriffe vom Windows gesperrt wird, fürs Speichern ist das dann nicht wünschenswert!
      // Lösung: Lade einfach die DLL in einen Buffer und verwende diesen anstelle der Datei selbst
      System.IO.FileInfo fileDatenbank = new System.IO.FileInfo(System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DatenbankFileName));
      if (fileDatenbank.Length > (long)int.MaxValue)
        throw new Exception("Datenbank hat das Maximum (2GB) der Größe überschritten.");
      int size = (int)fileDatenbank.Length;
      byte[] buffer = new byte[size];
      System.IO.FileStream streamFileDatenbank = new System.IO.FileStream(fileDatenbank.FullName, System.IO.FileMode.Open, System.IO.FileAccess.Read);
      streamFileDatenbank.Read(buffer, 0, size);
      streamFileDatenbank.Dispose();
      System.Reflection.Assembly asmDatenbank = System.Reflection.Assembly.Load(buffer);
      Type typeLoader = asmDatenbank.GetType(string.Concat(DatenbankName, ".", AddressenLoader));
      System.Activator.CreateInstance(typeLoader, this);
    }
  }

Und zu guter letzt die Adresse-Klasse:

Code:
  public class Adresse
  {
    public Adresse(params string[] data)
    {
      this.data = data;
    }
    public string Nachname
    {
      get { return this.data[0]; }
      set { this.data[0] = value; }
    }
    public string Vorname
    {
      get { return this.data[1]; }
      set { this.data[1] = value; }
    }
    public string Strasse
    {
      get { return this.data[2]; }
      set { this.data[2] = value; }
    }
    public string Postleitzahl
    {
      get { return this.data[3]; }
      set { this.data[3] = value; }
    }
    public string Ort
    {
      get { return this.data[4]; }
      set { this.data[4] = value; }
    }
    public string[] Daten
    {
      get { return this.data; }
    }
    public override string ToString()
    {
      return string.Concat(
        Vorname, " ", Nachname, "\r\n",
        Strasse, "\r\n",
        Postleitzahl, " ", Ort);
    }
    private string[] data;
  }
Auch wenn im Thread die Rede davon ist das es nicht möglich wäre, hör nicht drauf sondern versuch dein Glück! Am Ende bist du als Programmierer, derjenige der alles im Griff hat. Hürden gibts nur in der Phantasie...

Happy Coding...
 
Zuletzt bearbeitet:
Also da du anscheinend Anfänger bist, würde ich einmal schön langsam beginnen, indem du einfach eine Textdatei schreibst und liest.

Eine Datei kannst du mit der Funktion
System.IO.File.ReadAllText(Filename as String) as String
lesen

bzw. mit
System.IO.File.WriteAllText(Filename as String,Text as String,Overwrite as Boolean)
schreiben.

Vom Inhalt her würde ich eine CSV Datei hernehmen. Wenn du damit leben kannst, dass du in deinen Adressen keine Strichpunkte drin hast, dann ist das sehr einfach: http://de.wikipedia.org/wiki/CSV_(Dateiformat)

Zum Lesen:
1.) Den Text mit der Funktion ReadAllText auslesen.
2.) Den ausgelesenen Text mit der Funktion String.Split mit dem Parameter \n einmal in ein Feld von Zeilen zerteilen.
3.) Jeder der einzelnen Zeilen in einer Schleife durchgehen und wieder mit String.Split in die einzelnen Zellen Zerteilen

Zum Schreiben:
Alle Elemente einer Zeile mit Strichpunkten aneinander reihen.
Dann für alle Zeilen dasselbe noch einmal machen.

Das ganze kannst du dann abspeichern.

Wenn du das ganze dann noch etwas verbessern willst, kannst du:
1.) In der ersten Zeile nicht gleich die Daten, sondern schon die Spaltennamen hineinschreiben und dann anhand der Spaltennamen die Werte richtig einsortieren (damit man auch Spalten vertauschen kann).
2.) Beim Zeilenumbruch auch cr oder nur lf akzeptieren und Leerzeilen ignorieren
3.) Die Strichpunkte in den Daten richtig schützen, indem die den Wert in Hochkomma setzt und alle Hochkomma durch 2 Hochkomma ersetzt

Wenn du csv Dateien zum Testen anlegen willst, kannst du die auch in Excel erstellen und dann bei Speichern unter "csv Datei" auswählen.
 
Welche Version von .NET Framework man benötigt hängt nicht davon ab, welche Bibliotheken man verwendet, sondern für welche Version von .NET Framework das Projekt angelegt ist. Wenn man nicht zwingend Funktionen wie LINQ benötigt, sollte man es deshalb auf .NET 2.0 zurückstellen. Das ist im Gegensatz zum 3.5er ziemlich schnell installiert und auch auf den meisten Rechnern schon ob. Auf Vista ist standardmäßig auch nur 3.0 oben. Weiters läuft das 3.5er nicht auf Windows 2000.
Auf anderen Betriebssystemen als Windows wird ein C# Programm sowieso nicht laufen. Es gibt ein paar Versuche für Linux (Mono Projekt), aber das ist mehr eine Spielerei.
 
andr_gin schrieb:
Welche Version von .NET Framework man benötigt hängt nicht davon ab, welche Bibliotheken man verwendet, sondern für welche Version von .NET Framework das Projekt angelegt ist.
Tut mir leid. Ich habe mich unvollständig ausgedrückt.
 
Ja ist schon klar, wie du es gemeint hast, aber man sollte sich immer VORHER überlegen, für welche .NET Version man ein Projekt anlegt (2.0 oder 3.5 oder in Zukunft 4.0). Dann hat man gar nicht die Chance etwas falsches zu verwenden. Oft sind es auch nur Kleinigkeiten z.B. im .NET Framework 3.5 kann man für ein Feld wie für alle Collections die Länge mit .Count überprüfen. Das ist jedoch eine Extension und funktioniert nicht für .NET 2.0 und 3.0
Wenn man eine Bibliothek als .dll erzeugt, dann ist das besonders wichtig, weil niemand die Bibliothek verwenden kann, wenn er nicht das .NET Framework hochstellt. Das ist der Grund, warum ich trotz allen coolen neuen Features immer noch auf alles auf 2.0 programmiere, weil ich meine Haupt dll auch in Projekte integrieren will, wo nicht einfach so das 3.5er installieren kann z.B. nur ein kleines Tool für die Kommandozeile oder für bestehende Software auf Win2K Rechnern.
 
Aber ich wusste garnicht dass nicht jeder Win .NET bei höheren version unterstützt. Das ist natürlich blöd. Beim C++ MFC kann man ja noch sagen ab welche Win Version das Programm ausgeführt werden kann. Hmm naja aber Win 2000 hat glaube ich gar keiner mehr, der noch in diese welt lebt. Ich will auch nicht mal wissen wer das hat, weil es sooo instabil bei mir lief. Wenn ich mich noch an damals erinnere kommt mir jetzt alles viel einfacher vor mit den neuen Betriebssystemen.
 
Ja klar neu wird keiner mehr Win2K einsetzen, aber wenn man eine Software hat, die schon immer auf Win2K lief und es sich nur um eine kleine Erweiterung für ein bestehendes System handelt, dann sollte man sich gut überlegen, ob man das wirklich ausschließt.
Abgesehen davon ist der Installer beim .NET Framework 3.5 wirklich katastrohphal. Beim Offline Installer beglückt mich der z.B. immer noch mit der Meldung, dass das .NET Framework erfolgreich DEinstalliert wurde, abgesehen davon, dass es sehr lange dauert und besser nicht schiefgehen sollte. Bei einem Kunden werde ich mich davor hüten, das auf seinem produktiven Server zu installieren. Wenn da der Installer abbricht, ist es nämlich vorbei. Dann kann er den Server neu aufsetzen.
 
Zurück
Oben