#1 Recepta na – Multiple file extension

Niniejszy post rozpoczyna serię pt. „Recepta na”. Każdy post będzie dotyczył, jakiegoś małego problemu programistycznego i przykładowego rozwiązania. Takie małe, pomocne snippety.

Problem: Pobrać nazwę pliku bez jego rozszerzenia

.Net framework udostępnia metodę, która pozwala to jak najbardziej wykonać:

Path.GetExtension("sample.txt"); //-> ".txt"
Path.GetFileNameWithoutExtension("sample.txt"); //-> "sample"

Niestety nie wspiera ona wielu rozszerzeń w nazwie, np. sample.tar.gz

Path.GetExtension("sample.tar.gz"); //-> ".gz"
Path.GetFileNameWithoutExtension("sample.tar.gz"); //-> "sample.tar"

Jak spojrzymy w jej implementację zobaczymy, że poszukuję od końca przekazanej nazwy pierwszego wystąpienia kropki ’.’ i na tej podstawie zwraca substring od tego miejsca do końca. Dodatkowo wykonuje kilku walidacji. Link do implementacji

#1 Recepta

Najprostsze co mi przychodzi do głowy do napisać metodę, która będzie poszukiwała ’.’ zaczynając od początku nazwy i wszystko co następuje po nie potraktować jako rozszerzenie 😀

static string GetExtension(string filename)
{
    return filename.Substring(filename.IndexOf('.'), filename.Length - filename.IndexOf('.'));
}

Tak mogłaby wyglądać najprostsza implementacja, bez żadnych dodatkowych walidacji itp.
Ale przeczy to zasadzie a może intuicji że rozszerzenia poszukujemy od końca. Zbyt proste by mogło się sprawdzić.

#2 Recepta

Wykorzystać istniejąca metodę Path.GetExtension ale w taki sposób by wspierała wiele rozszerzeń. Zostaniemy przy „systemowej” implementacji co będzie dodatkowym atutem. Możemy to osiągnąć poprzez rekurencyjne wołanie w/w metody lub wykonanie tego w pętli. Rekurencja była zbyt prosta 😋 więc dziś przedstawię pętlę i to nie byle jaką – mianowicie do…while.

Rezultat końcowy:

static string GetFileNameWithoutExtension(string filename)
{
    int maxCounter = 20, loopIndex = 0;
    string nameOnly, currentName = filename;
    Stack<string> extensions = new Stack<string>();

    do
    {
        nameOnly = currentName;
        currentName = Path.GetFileNameWithoutExtension(nameOnly);
        extensions.Push(Path.GetExtension(nameOnly));
        loopIndex++;
    }
    while (nameOnly != currentName && loopIndex <= maxCounter);

    return nameOnly;
}

Wejściowa nazwa pliku jest „czyszczona”  z rozszerzenia do momentu aż wywołanie metody Path.GetExtension zwraca tą samą nazwę. Wtedy wiemy, że metoda już nic więcej nie ma do roboty.
Zawarłem tutaj jeszcze kilka rzeczy:

  • maxCounter – dodatkowe zabezpieczenie pętli aby uniknąć tzw. nieskończonej pętli
  • Stack<string> extensions – stos na który odkładane są rozszerzenia.