Autor Beitrag
lk1990
Hält's aus hier
Beiträge: 9



BeitragVerfasst: Fr 21.03.14 14:05 
Hi,

ich stehe wieder einmal vor einem Problem, von dem ich noch nicht genug verstehe, um es ohne Hilfe lösen zu können.

Derzeit möchte ich ein einfaches Tabellenmanipulationsprogramm programmieren. Zu diesem Zweck verwende ich ein nicht SQL oder sonst wie datenbankgebundenes DataGridView Steuerelement. Das Speichern und Laden der eingegebenen Werte erfolgt mit Hilfe von .txt Datein. Hier der entsprechende Code:

Laden:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
            string[] lines = System.IO.File.ReadAllLines(@"c:\test.txt");

            for (int i = 0; i <= 49; i++)
            {
                for (int j = 0; j <= 6; j++)
                {
                    dataGridView1[j, i].Value = (string)lines[i + 49 * j];
                }
            }


Beim Laden von den gespeicherten Werten aus einer Textdatei sollen die Werte Zeile für Zeile ausgelesen und dann mittels einer Funktion den entsprechenden Zellen der Tabelle (7 Spalten zu jeweils 50 Zeilenb) zugeordnet werden. Das Einlesen der Daten funktioniert einwandfrei. Wenn zwischen zwei Einlesevorgängen keine Änderungen gespeichert werden, dann werden die Zeilen korrekt den entsprechenden Zellen zugeordnet.


Speichern:
ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
        private void SaveChanges_Click(object sender, EventArgs e)
        {

            if (File.Exists(@"c:\test.txt"))
            {
                File.Delete(@"c:\test.txt");

                using (System.IO.StreamWriter file = new System.IO.StreamWriter(@"c:\test.txt"true))
                {
                    for (int i = 0; i <= 49; i++)
                    {
                        for (int j = 0; j <= 6; j++)
                        {
                            string cellValue = dataGridView1[j, i].Value.ToString();
                            file.WriteLine(cellValue);

                        }
                    }
                }
            }
            else
            {
                using (System.IO.StreamWriter file = new System.IO.StreamWriter("c:\\test.txt"true))
                {
                    for (int i = 0; i <= 49; i++)
                    {
                        for (int j = 0; j <= 6; j++)
                        {
                            string cellValue = dataGridView1[j, i].Value.ToString();
                            file.WriteLine(cellValue);

                        }
                    }
                }
            }
        }


Beim Speichern der Werte der Zellen der Tabelle soll jeder Wert einer besteimmten Zelle einer betimmten Zeile in einer .txt Datei so zugewiesen werden, dass jeweils 50 Zeilen den 50 Zellen einer Spalte entsprechen. Hier stehe ich allerding vor einem Problem: die Zurordnung Zelle->Zeile funktioniert nicht richtig. Ausser der Zelle (0,0) werden alle Zellen versetzt zugewiesen, die 49*n+1 (mit n element [1,..,5]) Zelle wird doppelt zugewiesen (sprich: der Inhalt der Zelle 49 wird benfalls in die Zelle 50 zugewiesen).


Ich würde mich über jede Hilfe freuen.
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4798
Erhaltene Danke: 1059

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Fr 21.03.14 14:31 
Hallo,

beim Einlesen hast du einen Index-Fehler, du hast doch 50 Zeilen
ausblenden C#-Quelltext
1:
lines[i + 50 * j]					


Daher verwende besser Konstanten und ändere die Schleifen ab zu:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
const int MaxRows = 50;
const int MaxCols = 7;

for (int i = 0; i < MaxRows; i++)
{
    for (int j = 0; j < MaxCols; j++)
    {
       // ...
    }
}


Und beim Speichern ist es überflüssig, daß du den Code doppelt hinschreibst, denn es reicht:
ausblenden C#-Quelltext
1:
2:
3:
4:
using (System.IO.StreamWriter file = new System.IO.StreamWriter(@"c:\test.txt"false))
{
  // ...
}

Beachte den Parameter false: damit wird immer eine neue Datei geschrieben bzw. die vorhandene überschrieben.
Und in das Root-Verzeichnis der Systempartition solltest du aber keine Dateien schreiben (anscheinend bist du Admin, sonst ginge es eh nicht).

Auch wenn du keine Datenbank benutzt, so solltest du doch datengebunden arbeiten, d.h. dir eine Klasse erstellen, welche die Daten hält (z.B. in einer DataTable).
lk1990 Threadstarter
Hält's aus hier
Beiträge: 9



BeitragVerfasst: Fr 21.03.14 14:49 
Danke für die Hilfe - jetzt funktioniert es.

Den Parameter true habe ich bei System.IO.StreamWriter file = new System.IO.StreamWriter(@"c:\test.txt"bool) gesetzt, damit jede Zeile neu an die bereits vorhandenen angehängt wird. Wenn der Parameter auf false steht, wird für jede Zelle eine neue .txt Datei erstellt und die alte überschrieben. Deswegen auch der doppelte Code - falls die Datei test.txt bereits existiert, dann muss sie, damit ich Zeile für Zeile hineinschreiben kann, ohne an die bei vorherigen Speichervorgängen ertsellten Zeilen anzuhängen, gelöscht und neu erstellt werden. Wird sie nicht gelöscht & neu erstellt, dann werden die neuen Zeilen an die bereits vorhanden Zeilen angefügt.

Edit: und danke für den Tipp mit DataTable.
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4798
Erhaltene Danke: 1059

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Fr 21.03.14 15:21 
Hallo,

schön, daß es jetzt klappt.

Deine Anmerkung bzgl. des Speicherns ist aber falsch. Probiere einfach mal meinen vereinfachten Code aus - du glaubst doch nicht, daß jedliche Speicherroutine den Code doppelt enthält? :gruebel:

Außerdem ist
ausblenden C#-Quelltext
1:
2:
3:
4:
if (File.Exists(@"c:\test.txt"))
{
    File.Delete(@"c:\test.txt");
}

ein Anti-Pattern und sogar ein Sicherheitsrisiko (security hole): Time of check to time of use.
lk1990 Threadstarter
Hält's aus hier
Beiträge: 9



BeitragVerfasst: Mi 02.04.14 16:52 
Bezogen auf das Anti-Pattern (und nur allgemein auf das Anti-Pattern und nicht auf den doppelten Streamwritercode, den ich vorhin gepostet habe): lt. dem Wikipedia Artikel lässt sich das Problem durch das Benutzen von Exceptionhandling anstatt Konditionsüberprüfung umgehen. Bedeutet das auf C# umgemünzt, dass zB

ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
try
{
File.Delete("C:\Path\file.txt");
}
catch(System.DirectoryNotFound, System.ArguementNullException)
{
MessageBox.Show("File not found""Error");
}


besser wäre als mein ursprünglicher Code?
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4798
Erhaltene Danke: 1059

Win10
C#, C++ (VS 2017/19/22)
BeitragVerfasst: Mi 02.04.14 19:32 
Hallo,

im generellen muß man bei IO-Zugriffen immer mit einer Exception rechnen, daher sollte man stets ein Exception-Handling in seinem Programm haben.
Dies bedeutet aber nicht, daß man nun jede Methode und jeden Zugriff mit try...catch umwickeln sollte, sondern meistens reicht ein allgemeines Abfangen in der GUI.

Bei File.Delete wird ja auch keine Exception geworfen, wenn die Datei nicht existiert - wohl aber wenn das Verzeichnis nicht existiert.
Sollte also das Verzeichnis z.B. vom Anwender frei eingegeben werden können, so sollte man entsprechend Abfragen durchführen (am besten sogar per doppelter Sicherung, d.h. zuersteinmal dem Anwender gar nicht erst erlauben, ungültige Pfade anzugeben und zum anderen dann bei konkretem Zugriff mögliche Exceptions anfangen).

Einige Exceptions wie ArgumentException, ArgumentNullException oder NullReferenceException sollte man nie explizit abfangen, denn diese sollten während des Debuggens schon gefunden werden und dann mit einer entsprechenden Abfrage vorher getestet werden (d.h. Programmierfehler sollte man also nicht kaschieren).