3 Möglichkeiten in C# in eine Datei zu schreiben

Nummer 2 verwende ich sehr gerne!
Veröffentlicht: vor einem monat · Lesedauer ca. 7 Minuten

Du schreibst ein Programm, alle Funktionen sind bereits fast implementiert und du bist mit deiner Arbeit auch zufrieden.

Aber dann kommt eine neue Anforderung:

Speichere doch mal bitte die Einstellungen in einer Datei ab, damit ich das nächste Mal nicht alles neu eingeben muss. Außerdem wäre es voll cool, dass Fehler protokolliert werden würden.

Okay, wie ging das noch einmal?

1. Der Klassiker – Mithilfe von Streams

Die wohl älteste Möglichkeit um in eine Datei zu schreiben, ist ein Stream. Dabei wird ein StreamWriter erstellt und anschließend der Inhalt in Form eines strings gespeichert.

Dies sieht in etwa so aus:

using (var config = new StreamWriter("config.txt"))
{
    config.WriteLine("Spieler 1: " + name1);
    config.WriteLine("Spieler 2: " + name2);
}

Das using ist in diesem Fall dafür zuständig den Stream richtig zu öffnen, zu schließen und den Inhalt (endgültig) in die Datei zu schreiben.

Für eine Konfiguration würde das schon funktionieren.

Eine Log-Datei würde ich mit dieser Methode jedenfalls nicht erstellen wollen.

Standardmäßig wird der komplette Inhalt der Datei vorher vom StreamWriter geleert.

Dies kann man umgehen, indem du dem StreamWriter beibringt etwas anzuhängen:

using (var fileStream = new FileStream("errors.log", FileMode.Append))
using (var logFile = new StreamWriter(fileStream))
{
	logFile.WriteLine("error: Es ist ein Fehler aufgetreten.");
}

Streams können aber noch viel mehr.

Beispielsweise kann auch ein binärer Inhalt (bspw. ein Bild) in eine Datei geschrieben werden. Dies erfolgt über den BinaryWriter:

byte[] buffer = new byte[] { 72,97,108,108,111,32,87,101,108,116 };

using (var fileStream = new FileStream("file.bin", FileMode.OpenOrCreate))
using (var binaryStream = new BinaryWriter(fileStream))
{
    binaryStream.Write(buffer);
}

Natürlich kann der binäre Inhalt auch direkt in einem Stream vorliegen. Dies wäre der MemoryStream. Dann würde das in etwa so aussehen:

byte[] buffer = new byte[] { 72,97,108,108,111,32,87,101,108,116 };

using (var memoryStream = new MemoryStream(buffer))
using (var fileStream = new FileStream("file.bin", FileMode.OpenOrCreate))
{
	memoryStream.WriteTo(fileStream);
}

Der große Vorteil bei Streams ist, dass man die Zugriffsrechte direkt mitsteuern kann.

2. Der Faule – Mit File.WriteAll

Wem dir der Umgang mit den Streams zu umständlich ist (davon gibt es ja so einige), kannst du auch die statischen Methoden aus dem dotNET-Framework verwenden.

Somit werden die oberen aufgaben jeweils zu Einzeilern und du musst dich nicht mehr um Zugriffsrechte etc. mehr kümmern.

Die entsprechenden „Funktionen“ sehen wie folgt aus:

// schreibt den Inhalt aus Config in die Datei
File.WriteAllText("config.txt", configString);

// fügt an das Ende der Datei einen Text an
File.AppendAllText("errors.log", "error: Es ist ein Fehler aufgetreten.");

// schreibt einen binären Inhalt in die Datei
File.WriteAllBytes("file.bin", buffer);

// erstellt eine Datei mit dem Inhalt eines String-Arrays
File.WriteAllLines("config.txt", configArray);

// fügt den Inhalt eines String-Arrays an das Ende der Datei hinzu
File.AppendAllLines("errors.log", errorMessages);

Wichtig zu Wissen ist, dass alle Methoden keine Zeilenumbrüche an das Ende der Datei machen. Da musst du dich selbst drum kümmern 🙂

3. Der Elegante – Mit ByteBee

In den meisten Fällen kommt man bereits mit den oberen Methoden sehr gut aus.

Spätestens bei UnitTests kannst du damit jedoch Probleme bekommen. Du musst dich nämlich um das Bereinigen der angefallenen Dateien kümmern. Selbst Dateien, die mit dem Test nichts zu tun haben, aber trotzdem angelegt wurden müssen bereinigt werden.

Das ByteBee Framework bietet extra hierfür Adapter an, die auf die dotNET Methoden verweisen.
Vorteil ist hierbei, dass du die Methoden überschreiben kannst.

Ein Beispielprogramm könnte in etwa so aussehen:

class FileShim : FileAdapter
{
	public override void AppendAllText(string path, string contents)
	{
		return;
	}
}

class Logger
{
	private IFile _file;
	private string _path;

	public Logger(IFile file, string path)
	{
		_file = file;
		_path = path;
	}

	public void Log(string message)
	{
		_file.AppendAllText(_path, message + Environment.NewLine);
	}
}

class Program
{
	static void Produktiv()
	{
		IFile file = SwissKnife.Fancy.file;
		var logger = new Logger(file, "errors.log");

		logger.Log("ich erscheine nirgens");
	}

	static void UnitTest()
	{
		IFile file = new FileShim();
		var logger = new Logger(file, "errors.log");

		logger.Log("ich erscheine nirgens");
	}
}

Natürlich muss man nicht jedes Mal, wenn man das ByteBee Framework benutzen möchte so einen Aufwand betreiben. Es soll nur die Möglichkeit dessen zeigen 🙂

Im ersten Schritt reicht es auch, wenn man den Code aus Beispiel 2 wie folgt ändert:

// schreibt den Inhalt aus Config in die Datei
file.WriteAllText("config.txt", configString);

// fügt an das Ende der Datei einen Text an
file.AppendAllText("errors.log", "error: Es ist ein Fehler aufgetreten.");

// Schreibt ein binären Inhalt in die Datei
file.WriteAllBytes("file.bin", buffer);

// Erstellt eine Datei mit dem Inhalt eines string-arrays
file.WriteAllLines("config.txt", configArray);

// fügt den Inhalt eines String-Arrays an das Ende der Datei hinzu
file.AppendAllLines("errors.log", errorMessages);

Beachte bitte die Groß- und Kleinschreibung bei file 🙂

Hierfür musst du aber zwingend ein weiteres using am Anfang der Datei hinzufügen. Visual Studio (selbst mit ReSharper) erkennen das nicht automatisch:

using static SwissKnife.Fancy;

Fazit

Ich hoffe, ich habe dich mit dem letzten Abschnitt nicht zu sehr verwirrt und du jetzt in der glücklichen Lage bist, Dateien nach Herzenslust zu schreiben.

Persönlich verwende ich in 70% der Fälle die zweite Alternative. Mit Streams arbeite ich nur, wenn es unabdingbar ist.

Die dritte Möglichkeit braucht man seltener als man denkt. Ein Logger ist ein Paradebeispiel dafür, wo man es brauchen könnte.

Interessanter wird das ganze, wenn man auf die Idee kommt, Dateien einzulesen und diese für UnitTests zu gebrauchen.

... und was meinst du dazu?
Deine E-Mail-Adresse wird nicht veröffentlicht.