using System.Collections.Generic;
using System.Collections.ObjectModel;
public ReadOnlyCollection<string> Segments { get; private set; }
public bool Directoryending { get; private set; }
public bool DirectoryStarting { get; private set; }
private FilePath _fileName;
if (_fileName == null && !Directoryending)
_fileName = Segments.LastOrDefault();
private FilePath _directory;
public FilePath Directory
List<string> segments = new List<string>(Segments);
segments.RemoveAt(segments.Count - 1);
_directory = new FilePath(segments, DirectoryStarting, true);
private bool? _isAbsolute;
if (!_isAbsolute.HasValue)
(Segments.Count > 0) _isAbsolute = Segments[0][Segments[0].Length - 1] == ':';
else _isAbsolute = false;
return _isAbsolute.Value;
protected FilePath(IList<string> segments, bool directoryStarting, bool directoryEnding)
Segments = new ReadOnlyCollection<string>(segments);
this.DirectoryStarting = directoryStarting;
this.Directoryending = directoryEnding;
public FilePath(string value)
Segments = new ReadOnlyCollection<string>(new List<string>());
char end = value[value.Length - 1];
if (start == '\\' || start == '/')
DirectoryStarting = value.Length > 1;
if (end == '\\' || end == '/') Directoryending = true;
Segments = new ReadOnlyCollection<string>(value.Split(new[] { '\\', '/' }, StringSplitOptions.RemoveEmptyEntries).ToList());
public bool Contains(FilePath path)
if (IsAbsolute == !path.IsAbsolute)
if (path.Segments.Count < Segments.Count)
for (int _ = 0; _ < Segments.Count; _++)
if (!string.Equals(Segments[_], path.Segments[_], StringComparison.OrdinalIgnoreCase))
public FilePath Combine(params FilePath[] parts)
List<string> segments = new List<string>(Segments);
foreach (var part in parts)
if (part.IsAbsolute) throw new Exception("Exected relative path: " + part);
segments.AddRange(part.Segments);
dirending = part.Directoryending;
return new FilePath(segments, DirectoryStarting, dirending);
public FilePath MakeRelative(FilePath root)
if (!IsAbsolute) throw new Exception("Exected absolute path: " + this);
if (!root.IsAbsolute || root.Segments[0] != Segments[0])
List<string> segments = new List<string>();
int len = Math.Min(Segments.Count, root.Segments.Count);
if (!string.Equals(Segments[i], root.Segments[i], StringComparison.OrdinalIgnoreCase)) break;
for (int j = i; j < root.Segments.Count; j++)
for (; i < Segments.Count; i++) segments.Add(Segments[i]);
return new FilePath(segments, false, Directoryending);
public FilePath _makeabsolute(FilePath root = null)
root = new FilePath(System.IO.Directory.GetCurrentDirectory());
return root.Combine(this);
public FilePath Makedir(bool directoryending = true)
if (this.Directoryending == directoryending)
return new FilePath(Segments, DirectoryStarting, directoryending);
protected void ApplyCommands(List<string> segments)
for (int _ = 0; _ < segments.Count; _++)
case "~": segments.RemoveAt(_);
if (_ > 1 && segments[_ - 1] != "..")
segments.RemoveAt(_); segments.RemoveAt(_ - 1); _ -= 2;
private string _tostring;
public override string ToString()
return _tostring ?? (_tostring = string.Format("{0}{1}{2}", DirectoryStarting ? "\\" : "", string.Join("\\", Segments), Directoryending ? "\\" : ""));
public override bool Equals(object obj)
if (ReferenceEquals(this, obj)) return true;
FilePath b = obj as FilePath;
return b != null && Segments.
SequenceEqual(b.Segments, StringComparer.OrdinalIgnoreCase) && DirectoryStarting == b.DirectoryStarting && Directoryending == b.Directoryending;
public override int GetHashCode()
int res = Directoryending ? 1 : 0;
res += DirectoryStarting ? 1 : 0;
foreach (var seg in Segments) unchecked
res += StringComparer.OrdinalIgnoreCase.GetHashCode(seg);
public static implicit operator string(FilePath path)
public static implicit operator FilePath(string path)
return new FilePath(path);
public static void Main(string[] args)
const string s = @"c:\test\test2\test3\a.txt";
Assert_areEqual(5, path1.Segments.Count);
Assert_areEqual(true, path1.IsAbsolute);
Assert_areEqual("a.txt", path1.FileName.ToString());
Assert_areEqual(s, path1.ToString());
FilePath folder = path1.Directory;
Assert_areEqual(@"c:\test\test2\test3\", folder.ToString());
path1 = folder.Combine(@"~\test4\test5\");
Assert_areEqual(@"c:\test\test2\test3\test4\test5\", path1.ToString());
Assert_areEqual(@"test4\test5\", path1.MakeRelative(folder).ToString());
Assert_areEqual(@"..\test3\test4\test5\", path1.MakeRelative(@"c:\test\test2\test0\").ToString());
FilePath path2 = new FilePath(@"c:\test\test2\test0\").Combine(@"..\test3\test4\test5\");
Assert_areEqual(path1, path2);
Console.WriteLine("Tests complete!");
private static void Assert_areEqual(object expected, object actual)
if (!Equals(expected, actual))
throw new Exception("Asserion failed: expected " + expected + ",but got " + actual);