C# – Salva un elenco di stringhe in un file

 C Programming >> Programmazione C >  >> Tags >> File
C# – Salva un elenco di stringhe in un file

Il modo più semplice per salvare un elenco di stringhe in un file è utilizzare File.WriteAllLines().

var ipAddresses = new List<string>()
{
	"127.0.0.1",
	"127.0.0.10",
	"127.0.0.17"
};

System.IO.File.WriteAllLines(@"C:\temp\ipAddresses.txt", ipAddresses);
Code language: C# (cs)

Questo crea (o sovrascrive) il file specificato e scrive ogni stringa su una nuova riga. Il file risultante è simile al seguente:

127.0.0.1\r\n
127.0.0.10\r\n
127.0.0.17\r\n
Code language: plaintext (plaintext)

Nota:vengono visualizzati caratteri di nuova riga non stampabili \r\n per chiarezza.

Specifica del carattere separatore

E se volessi separare ogni stringa con una virgola (o qualche altro carattere separatore a tua scelta), invece di scrivere ogni stringa su una nuova riga?

Per fare ciò, puoi unire le stringhe e specificare il carattere separatore che desideri utilizzare, quindi utilizzare File.WriteAllText().

var ipAddresses = new List<string>()
{
	"127.0.0.1",
	"127.0.0.10",
	"127.0.0.17"
};

var commaSeparatedIPs = string.Join(',', ipAddresses);

System.IO.File.WriteAllText(@"C:\temp\ipAddresses.txt", commaSeparatedIPs);
Code language: C# (cs)

Questo crea (o sovrascrive) il file specificato, emettendo le stringhe separate da una virgola:

127.0.0.1,127.0.0.10,127.0.0.17Code language: plaintext (plaintext)

Lettura delle stringhe da un file in un elenco

Quando ogni stringa è su una nuova riga

Per leggere le stringhe da un file in un elenco, puoi utilizzare File.ReadAllLines(), che restituisce una matrice di stringhe. Se stai solo elaborando le stringhe e non hai bisogno di tenerle in memoria, usa invece File.ReadLines() per ottenere un IEnumerable.

//As an array
string[] ipAddressesArray = System.IO.File.ReadAllLines(@"C:\temp\ipAddresses.txt");

//As a list
using System.Linq;
List<string> ipAddresses = System.IO.File.ReadAllLines(@"C:\temp\ipAddresses.txt").ToList();

//As an enumerable if you don't need to keep the strings around
IEnumerable<string> ipAddresses = System.IO.File.ReadLines(@"C:\temp\ipAddresses.txt");
Code language: C# (cs)

Quando le stringhe sono separate con un carattere diverso

Per riportare le stringhe in un elenco, devi leggere il file e dividere la stringa con il carattere separatore.

//As an array
string[] ipAddresses = System.IO.File.ReadAllText(@"C:\temp\ipAddresses.txt").Split(',');

//As a list
using System.Linq;
var ipAddresses = System.IO.File.ReadAllText(@"C:\temp\ipAddresses.txt").Split(',').ToList();
Code language: C# (cs)

Si noti che questo sta leggendo l'intero file. Ciò è necessario perché non esiste una funzione incorporata di alto livello equivalente a File.ReadLines() che consente di specificare un separatore diverso. Se non vuoi leggere l'intero file in memoria contemporaneamente in questo scenario, consulta il metodo del generatore di seguito.

Ottieni un IEnumerable quando usi un carattere separatore diverso

Se non vuoi leggere l'intero file in memoria e hai a che fare con caratteri separati da una nuova riga, puoi utilizzare il seguente metodo di generazione ReadStrings(). Questo legge i blocchi di caratteri dal flusso di file e cerca i caratteri separatori. Una volta incontrato un separatore, restituisce una stringa.

using System.IO;

public static IEnumerable<string> ReadStrings(string path, char separator)
{
	var sb = new StringBuilder();
	using (var sr = new StreamReader(path))
	{
		char[] buffer = new char[1024];
		int charsRead = 0;

		//Keep track of how many chars to copy into StringBuilder
		int charBlockIndex = 0;
		int charBlockCount = 0;

		while (!sr.EndOfStream)
		{
			charBlockIndex = 0;
			charBlockCount = 0;
			charsRead = sr.Read(buffer, 0, buffer.Length);
			for (int i = 0; i < charsRead; i++)
			{
				if (buffer[i] == separator)
				{
					//Once a separator is found, copy block to StringBuilder and yield it
					sb.Append(buffer, charBlockIndex, charBlockCount);
					yield return sb.ToString();
					sb.Clear();
					charBlockIndex = i + 1;
					charBlockCount = 0;
				}
				else
				{
					charBlockCount++;
				}
			}

			//Copy remaining chars since separator was found
			if (charBlockCount > 0)
				sb.Append(buffer, charBlockIndex, charBlockCount);
		}

		if (sb.Length > 0)
			yield return sb.ToString();
	}
	yield break;
}
Code language: C# (cs)

Nota:invece di copiare un carattere alla volta in StringBuilder, quando incontra un separatore (o esaurisce i caratteri nel buffer), copia blocchi di caratteri dal buffer allo StringBuilder. Questo è più difficile da capire, ma migliora un po' le prestazioni.

Ecco il confronto delle prestazioni tra questo metodo generatore e l'approccio ReadAllText().Split():

|            Method | NumStrings |       Mean |  Allocated |
|------------------ |----------- |-----------:|-----------:|
| ReadAllText_Split |      10000 |   2.771 ms |   2,562 KB |
|         Generator |      10000 |   2.291 ms |     947 KB |

| ReadAllText_Split |     100000 |  42.998 ms |  25,440 KB |
|         Generator |     100000 |  22.552 ms |   9,385 KB |

| ReadAllText_Split |    1000000 | 419.261 ms | 254,254 KB |
|         Generator |    1000000 | 235.808 ms |  93,760 KB |Code language: plaintext (plaintext)

Il generatore è circa 2 volte più veloce e alloca complessivamente molta meno memoria. Ancora più importante, il generatore riduce al minimo l'utilizzo della memoria per tutta la durata del processo. Nel test da 1 milione di stringhe, il processo generatore ha utilizzato un massimo di 8 MB, mentre il processo ReadAllText().Split() ha utilizzato 200 MB.