Introduction
In modern software development, efficient data management is essential. While relational database management systems like SQL servers are often the go-to solution, simpler scenarios sometimes require a more lightweight approach. For example, applications that do not demand the complexity of relational databases can benefit from using text files for data storage. This is the case with our Phone Book application, which manages data using a simple 2D table. To streamline the process of saving and retrieving data, we developed the ArrayIOExtensionsLib, a library that provides easy-to-use methods for handling data stored in text files.
Overview of ArrayIOExtensionsLib
ArrayIOExtensionsLib is a .NET library designed to simplify working with arrays stored in text files. It provides extension methods for saving and loading both single and multi-dimensional arrays of simple types (such as strings and primitives) to and from text files. This library is ideal for applications that do not require a relational database but still need reliable and efficient data storage and retrieval.
Key Features
- Save and Load Multi-dimensional Arrays: Save arrays of any rank (1D, 2D, etc.) to text files, where each element is stored as a separate line.
- Support only for Simple Types: Works with arrays of strings, primitives (int, double, etc.), and custom object types that include only simple properties.
- Fixed-length Arrays: The library works with fixed-length arrays. The programmer must anticipate the maximum number of records the array will hold at runtime, as arrays cannot dynamically resize. If the array's size needs to be expanded, a new, larger array must be created, and existing data should be copied over to the new array. This can be done by manually resizing the array and transferring data or by implementing logic to periodically check if the array is full and perform the resize operation as needed.
- Null Handling: null values are serialized as "null" in the text file and correctly deserialized back into the array.
- Customizable Encoding: Allows specifying encoding when saving and loading files (defaults to UTF-8).
Important Notice. All array fields must be properly initialized before using the extension methods in this library. Uninitialized arrays or null values may lead to unexpected behavior during serialization and deserialization.
Installation
To use this library in your project.
- Download ArrayIOExtensionsLib open-source archive
- Build the project to generate the .dll file.
- Reference the generated .dll in your own project.
- In Visual Studio, Right-click your project > Add > Reference > Browse to select the generated .dll.
- In .NET CLI, Run the following command.
dotnet add reference /path/to/ArrayIOExtensions.dll
Usage
Save an Array to a Text File.
int[,] myArray = new int[,]
{
{ 1, 2 },
{ 3, 4 },
{ 5, 6 }
};
myArray.SaveToTextFile("output.txt");
Load an Array from a Text File.
int[,] myArray = new int[3, 2];
myArray.LoadFromTextFile("output.txt");
Saving Custom Types.
public class Customer
{
public string? Name { get; set; }
public string? Address { get; set; }
}
Customer[] customers = new Customer[2];
customers[0] = new Customer
{
Name = "John",
Address = "123 Street"
};
customers[1] = new Customer
{
Name = "Jane",
Address = "456 Avenue"
};
customers.SaveToTextFile("customers.txt");
Loading Custom Types.
Customer[] customers = new Customer[2];
customers.LoadFromTextFile("customers.txt");
Code Breakdown
Here is a detailed explanation of the ArrayIOExtensions class code, method by method.
using System.IO;
using System.Text;
using System;
namespace ArrayIOExtensionsLib
{
/// <summary>
/// Provides extension methods for saving and loading
/// single or multi-dimensional arrays to and from text files.
/// IMPORTANT: Values in the array must be properly initialized
/// before calling the save methods, especially when working with
/// arrays of complex or custom types. If not properly initialized,
/// null values in objects may lead to incorrect written values
/// in the text file, as the object will not be serialized with
/// all its properties.
/// </summary>
public static class ArrayIOExtensions
{
#region Properties
/// <summary>
/// Represents the default encoding used for saving arrays to text files.
/// </summary>
private static readonly Encoding _defaultEncoding = Encoding.UTF8;
/// <summary>
/// Represents the default word used for null value.
/// </summary>
private static readonly string _nullRepresentation = "null";
#endregion
}
}
Properties
The ArrayIOExtensionsLib library relies on a couple of key properties to ensure smooth operation during file I/O processes:
- _defaultEncoding: Specifies UTF-8 as the default encoding format for saving and reading text files.
- _nullRepresentation: Defines how null values are represented in the text file. By default, the string "null" is used.
Saving Arrays to Text Files
The primary method for saving arrays is SaveToTextFile.
Important. Values in the array must be properly initialized before calling the save methods, especially when working with arrays of complex or custom types. If not properly initialized, null values in objects may lead to incorrect written values in the text file, as the object will not be serialized with all its properties.
#region Save To Text File
/// <summary>
/// Saves a single or multi-dimensional array to a specified text file.
/// Each element of the array is written to a new line in the file.
/// IMPORTANT: Values in the array must be properly initialized before
/// calling the save methods, especially when working with arrays
/// of complex or custom types. If not properly initialized,
/// null values in objects may lead to incorrect written values
/// in the text file, as the object will not be serialized
/// with all its properties.
/// </summary>
/// <param name="array">The single or multi-dimensional array to save.</param>
/// <param name="filePath">The path of the file where the array will be saved.</param>
/// <param name="encoding">Encoding used for saving arrays to text files.</param>
/// <exception cref="ArgumentNullException">Thrown when the array is null.</exception>
/// <exception cref="ArgumentException">Thrown when the file path is null or whitespace.</exception>
public static void SaveToTextFile(this Array array,
string filePath,
Encoding? encoding = null)
{
// Validate the array and file path before proceeding.
ValidateArrayAndFilePath(array, filePath);
try
{
// Check encoding and set the default encoding if not specified.
encoding ??= _defaultEncoding;
// Create a StreamWriter to write to the specified file using UTF-8 encoding.
using var writer = new StreamWriter(filePath, false, encoding);
// Start the process of saving the array to the text file.
SaveArrayToTextFile(array, writer);
}
catch (Exception ex)
{
// Throw an exception if an error occurs during file writing.
throw new ArgumentException(
$"An error occurred while saving to file: {ex.Message}");
}
}
SaveToTextFile Method
- This method saves an array (either single or multi-dimensional) to a specified text file.
- It first validates the input array and file path using the ValidateArrayAndFilePath method.
- It then checks the specified encoding, defaulting to UTF-8 if not provided.
- A StreamWriter is used to write each element of the array to a new line in the text file by calling the SaveArrayToTextFile method.
Internal Method for Saving
/// <summary>
/// Initializes the dimensions array and starts the recursive saving of the array.
/// </summary>
/// <param name="array">The single or multi-dimensional array to save.</param>
/// <param name="writer">The StreamWriter used for writing to the file.</param>
private static void SaveArrayToTextFile(Array array, StreamWriter writer)
{
// Create an array to hold the length
// of each dimension of the array.
int[] dimensions = new int[array.Rank];
for (int i = 0; i < array.Rank; i++)
{
dimensions[i] = array.GetLength(i);
}
// Get the element type of the array.
var elementType = array.GetType().GetElementType();
// Begin the recursive method to save
// the array elements to the text file.
SaveArrayToTextFileRecursive(array,
writer,
dimensions,
new int[array.Rank],
0,
elementType!);
}
SaveArrayToTextFile Method
- This method initializes an array to hold the lengths of each dimension of the input array.
- It then calls the SaveArrayToTextFileRecursive method to handle the recursive saving of the array elements.
Recursive Saving Logic
/// <summary>
/// Recursively writes the contents of
/// the single or multi-dimensional array to the StreamWriter.
/// </summary>
/// <param name="array">The single or multi-dimensional array to save.</param>
/// <param name="writer">The StreamWriter used for writing to the file.</param>
/// <param name="dimensions">The lengths of each dimension of the array.</param>
/// <param name="indices">The current indices in the array being processed.</param>
/// <param name="currentIndex">The current dimension index being processed.</param>
/// <param name="elementType">The type of the object to serialize.</param>
private static void SaveArrayToTextFileRecursive(Array array,
StreamWriter writer,
int[] dimensions,
int[] indices,
int currentIndex,
Type elementType)
{
// Shortcut to handle single-dimensional arrays directly
if (array.Rank == 1)
{
for (int i = 0; i < array.Length; i++)
{
// Get the value of the single-dimensional array
var value = array.GetValue(i);
// Serialize the value
SerializeObject(writer, value, elementType);
}
return;
// Exit early since we've already processed the entire array
}
// If we are at the last dimension, write the values to the file.
if (currentIndex == array.Rank - 1)
{
for (int i = 0; i < dimensions[currentIndex]; i++)
{
indices[currentIndex] = i; // Set the index for the last dimension.
var value = array.GetValue(indices);
SerializeObject(writer, value, elementType);
}
}
else
{
// Iterate through the current dimension
// and recurse into the next dimension.
for (int i = 0; i < dimensions[currentIndex]; i++)
{
indices[currentIndex] = i;
SaveArrayToTextFileRecursive(array,
writer,
dimensions,
indices,
currentIndex + 1,
elementType);
}
}
}
#endregion
SaveArrayToTextFileRecursive Method
- This method handles the recursive writing of array elements to the StreamWriter.
- It first checks if the array is single-dimensional, saving values directly if so.
- For multi-dimensional arrays, it writes values from the last dimension and recurses for previous dimensions, building up the index as it goes.
Serialization
/// <summary>
/// Serializes an object to the StreamWriter.
/// </summary>
/// <param name="writer">The StreamWriter used for writing to the file.</param>
/// <param name="obj">The object to serialize.</param>
/// <param name="elementType">The type of the object to serialize.</param>
private static void SerializeObject(StreamWriter writer,
object? obj,
Type elementType)
{
// Check if the object is null
if (obj == null)
{
if (elementType == typeof(string) || elementType.IsPrimitive)
{
writer.WriteLine(_nullRepresentation);
return;
}
// Attempt to create a new instance if it is a complex type
// Assuming you know the type or have a way to get it
obj = Activator.CreateInstance(elementType);
}
// Now serialize the object
if (obj is string || obj!.GetType().IsPrimitive)
{
writer.WriteLine(obj.ToString());
}
else
{
// Write each property on a new line
var properties = obj.GetType().GetProperties();
foreach (var property in properties)
{
var value =
property.GetValue(obj)?.ToString() ?? _nullRepresentation;
writer.WriteLine(value);
}
}
}
SerializeObject Method
- This method handles the serialization of individual objects, writing their properties to the text file.
- If the object is null, it writes "null"; otherwise, it writes the value or each property of the object on a new line.
Important. Values in the array must be properly initialized before calling the save methods, especially when working with arrays of complex or custom types. If not properly initialized, null values in objects may lead to incorrect written values in the text file, as the object will not be serialized with all its properties.
Loading Arrays from Text Files
The method for loading arrays is LoadFromTextFile.
#region Load From Text File
/// <summary>
/// Loads data from a specified text file and populates the single or multi-dimensional array.
/// Each line in the file corresponds to a single element of the array.
/// </summary>
/// <param name="array">The single or multi-dimensional array to populate.</param>
/// <param name="filePath">The path of the file from which to load the array.</param>
/// <param name="encoding">Encoding used for reading the text file.</param>
/// <exception cref="ArgumentNullException">Thrown when the array is null.</exception>
/// <exception cref="ArgumentException">Thrown when the file path is null or whitespace.</exception>
public static void LoadFromTextFile(this Array array,
string filePath,
Encoding? encoding = null)
{
// Validate the array and file path before proceeding.
ValidateArrayAndFilePath(array, filePath);
try
{
// Start the process of loading the array from the text file.
LoadArrayFromTextFile(array, filePath, encoding);
}
catch (Exception ex)
{
// Throw an exception if an error occurs during file reading.
throw new ArgumentException(
$"An error occurred while loading from file: {ex.Message}");
}
}
LoadFromTextFile Method
- This method loads data from a text file and populates the specified array.
- Similar to SaveToTextFile, it validates the input and then calls the LoadArrayFromTextFile method to perform the loading.
Internal Method for Loading
/// <summary>
/// Loads the single or multi-dimensional array from the specified file.
/// Each line in the file corresponds to a single element of the array.
/// </summary>
/// <param name="array">The single or multi-dimensional array to populate.</param>
/// <param name="filePath">The path of the file from which to load the array.</param>
/// <param name="encoding">Encoding used for reading the text file.</param>
private static void LoadArrayFromTextFile(Array array,
string filePath,
Encoding? encoding)
{
try
{
// Check encoding and set the default encoding if not specified.
// Default encoding is UTF-8.
encoding ??= Encoding.UTF8;
// Read all lines from the specified file using the specified encoding.
var lines = File.ReadAllLines(filePath, encoding);
// Get the element type of the array.
var elementType = array.GetType().GetElementType();
// Validate that the number of lines read matches
// the total number of elements in the array.
// This also accounts for complex types with multiple properties.
ValidateArrayAndFileLength(array.Length, lines.Length, elementType!);
// Fill the array with the values from the lines.
var lineIndex = 0;
FillArray(array,
lines,
ref lineIndex,
new int[array.Rank],
0,
elementType!);
}
catch (Exception ex)
{
// Throw an exception if an error occurs during file reading.
throw new ArgumentException(
$"An error occurred while loading from file: {ex.Message}");
}
}

Christian AshishPosted Nov 14, 2024, 11:03 AM
This is a great article that provides a clear and concise overview of the ArrayIOExtensionsLib class. I found the explanation of how to convert arrays to text files in C# to be very helpful. The ArrayIOExtensionsLib class seems like a valuable tool for anyone who works with arrays in C#. Thanks for sharing!