Gapotchenko.FX.IO 2022.2.7

Prefix Reserved
dotnet add package Gapotchenko.FX.IO --version 2022.2.7                
NuGet\Install-Package Gapotchenko.FX.IO -Version 2022.2.7                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Gapotchenko.FX.IO" Version="2022.2.7" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Gapotchenko.FX.IO --version 2022.2.7                
#r "nuget: Gapotchenko.FX.IO, 2022.2.7"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Gapotchenko.FX.IO as a Cake Addin
#addin nuget:?package=Gapotchenko.FX.IO&version=2022.2.7

// Install Gapotchenko.FX.IO as a Cake Tool
#tool nuget:?package=Gapotchenko.FX.IO&version=2022.2.7                

Overview

The module provides highly demanded input/output functionality that is missing in conventional .NET platform.

FileSystem

FileSystem is a class provided by Gapotchenko.FX.IO. If offers extended I/O functions for file system, and serves as an important addendum to a conventional System.IO namespace.

IsCaseSensitive

The FileSystem.IsCaseSensitive property shows whether the current host operating system uses case sensitive file names.

For example, Windows operating system uses case-insensitive file names, so FileSystem.IsCaseSensitive returns false. The same goes to macOS.

However, Linux and other Unix flavors use case-sensitive file names by default. Whenever an app creates two files named Test.txt and test.txt, those files are distinct and can coexist at the same folder. FileSystem.IsCaseSensitive returns true on such operating systems.

PathComparer

FileSystem.PathComparer property returns a string comparer for file names.

Please take a look at the code below:

using Gapotchenko.FX.IO;

var files = new HashSet<string>(FileSystem.PathComparer);

files.Add("Test.txt");
files.Add("test.txt");

Console.WriteLine("Count of files: {0}", files.Count);

The given set would contain one entry on Windows, and two entries on Linux.

PathComparison

FileSystem.PathComparison property returns a StringComparison value that signifies a file name comparison mode used by the host OS.

It can be used in string comparison operations:

using Gapotchenko.FX.IO;

void ProcessFile(string filePath)
{
    if (!filePath.EndsWith(".txt", FileSystem.PathComparison))
        throw new Exception("Only text files can be processed.");
    …
}

PathsAreEquivalent(string a, string b)

Determines whether the given paths are equivalent. If they point to the same file system entry then the method returns true; otherwise, false.

The problem this method solves is caused by the fact that a file path can be specified in multiple forms:

  • Test.txt (relative path)
  • C:\Temp\Test.txt (absolute path)

Let's take a look at code:

using Gapotchenko.FX.IO;

Directory.SetCurrentDirectory(@"C:\Temp");

string fileA = "Test.txt";
string fileB = @"C:\Temp\Test.txt";

Console.WriteLine("String equality: {0}", string.Equals(fileA, fileB));
Console.WriteLine("Path equivalence: {0}", FileSystem.PathsAreEquivalent(fileA, fileB));

It produces the following results:

String equality: False
Path equivalence: True

Note that the file equivalence check is positive despite the different forms of a file path.

PathStartsWith(string path, string value)

Determines whether the beginning of the path matches the specified value in terms of the file system equivalence.

Say we have a folder name Contoso\Reports\2012\Final. How do we know that it starts with Contoso\Reports?

A straightforward solution would be to use String.StartsWith function, like so:

bool IsContosoReportsFolder(string path) => path.StartsWith(@"Contoso\Reports");

It kind of works, until we try to pass something like Contoso\ReportsBackup. The problem is that ReportsBackup is a very different folder than Reports, but the provided function returns true nevertheless.

We can cheat here, and try to use an updated function that adds a trailing slash:

bool IsContosoReportsFolder(string path) => path.StartsWith(@"Contoso\Reports\");

The problem is gone. Until we ask for IsContosoReportsFolder("Contoso\Reports") value. It is false now despite the fact that Contoso\Reports is literally the folder we are so eagerly looking for.

The correct solution is to use FileSystem.PathStartsWith method provided by Gapotchenko.FX.IO module:

using Gapotchenko.FX.IO;

bool IsContosoReportsFolder(string path) => FileSystem.PathStartsWith(path, @"Contoso\Reports");

It will now give the correct results for all inputs, even when they use alternative directory separators:

IsContosoReportsFolder(@"Contoso\ReportsBackup") => false
IsContosoReportsFolder(@"Contoso\Reports\2012\Final") => true
IsContosoReportsFolder(@"Contoso\Reports") => true
IsContosoReportsFolder(@"Contoso/Reports/2019/Progress") => true

WaitForFileWriteAccess(path)

FileSystem.WaitForFileWriteAccess method is a subtle but important primitive. It waits for a write access to the specified file.

Why would anyone want such a method? It turns out that a modern OS is a noisy environment that can put your app under a sledgehammer.

For example, if an app changes a file, it immediately grabs attention of various OS services. Anti-virus tools, search engines, file synchronization applications all can lock the files for short random time spans.

If a user of your app is unlucky or just uses an app frequently enough then he would occasionally get "File access denied" errors.

To minimize a possibility of such a congestion, you should call FileSystem.WaitForFileWriteAccess method before changing a file:

using Gapotchenko.FX.IO;

string fileName = "Results.txt";
FileSystem.WaitForFileWriteAccess(fileName);
File.WriteAllText(fileName, "A user can now use the app without occasional 'File access denied' errors.");

What it does is polls the file until write access is available. If the access is not there for 10 seconds, the method falls through.

More on this topic (Raymond Chen, "The Old New Thing" blog)

BitReader/BitWriter

BitReader and BitWriter classes provided by Gapotchenko.FX.IO extend the functionality of conventional BinaryReader and BinaryWriter by inheriting from them.

The conventional BinaryReader/BinaryWriter combo only supports little-endian byte order. However, big-endian byte order is equally widespread.

This is how a big-endian binary reader can be created:

using Gapotchenko.FX;
using Gapotchenko.FX.IO;

var br = new BitReader(BigEndianBitConverter.Instance);

Thanks to the fact that BitReader is inherited from BinaryReader class, it is almost a drop-in replacement. The same goes to BitWriter.

Commonly Used Types

  • Gapotchenko.FX.IO.FileSystem
  • Gapotchenko.FX.IO.BitReader
  • Gapotchenko.FX.IO.BitWriter

Other Modules

Let's continue with a look at some other modules provided by Gapotchenko.FX:

Or look at the full list of modules.

Product Compatible and additional computed target framework versions.
.NET net5.0 is compatible.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 is compatible.  netcoreapp2.1 is compatible.  netcoreapp2.2 was computed.  netcoreapp3.0 is compatible.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net46 is compatible.  net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 is compatible.  net472 is compatible.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Gapotchenko.FX.IO:

Package Downloads
Gapotchenko.FX.Profiles.Core

Represents the Core profile of Gapotchenko.FX.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
2022.2.7 847 5/1/2022
2022.2.5 757 5/1/2022
2022.1.4 757 4/6/2022
2021.2.21 825 1/21/2022
2021.2.20 740 1/17/2022
2021.1.5 618 7/6/2021
2020.2.2-beta 451 11/21/2020
2020.1.15 770 11/5/2020
2020.1.9-beta 504 7/14/2020
2020.1.8-beta 495 7/14/2020
2020.1.7-beta 523 7/14/2020
2020.1.1-beta 586 2/11/2020
2019.3.7 779 11/4/2019
2019.2.20 746 8/13/2019
2019.1.151 887 3/30/2019