Biorąc pod uwagę ścieżkę systemu plików, czy istnieje krótszy sposób na wyodrębnienie nazwy pliku bez rozszerzenia?

Programuję w WPF C#. Mam np. następującą ścieżkę:

C:\Program Files\hello.txt

And I want to extract hello od tego.

Ścieżka jest string pobierana z bazy danych. Obecnie używam poniższego kodu, aby podzielić ścieżkę przez '\', a następnie ponownie podzielić przez '.':

string path = "C:\\Program Files\\hello.txt";
string[] pathArr = path.Split('\\');
string[] fileArr = pathArr.Last().Split('.');
string fileName = fileArr.Last().ToString();
To działa, ale uważam, że powinno być na to krótsze i mądrzejsze rozwiązanie. Jakiś pomysł?
Author: Lance U. Matthews, 2011-08-03

11 answers

 520
Author: Christopher Currens,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-04-19 07:14:44
 91
Author: Yahia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-03 02:48:58

Try

System.IO.Path.GetFileNameWithoutExtension(path); 

Demo

string fileName = @"C:\mydir\myfile.ext";
string path = @"C:\mydir\";
string result;

result = Path.GetFileNameWithoutExtension(fileName);
Console.WriteLine("GetFileNameWithoutExtension('{0}') returns '{1}'", 
    fileName, result);

result = Path.GetFileName(path);
Console.WriteLine("GetFileName('{0}') returns '{1}'", 
    path, result);

// This code produces output similar to the following:
//
// GetFileNameWithoutExtension('C:\mydir\myfile.ext') returns 'myfile'
// GetFileName('C:\mydir\') returns ''

Https://msdn.microsoft.com/en-gb/library/system.io.path.getfilenamewithoutextension%28v=vs.80%29.aspx

 30
Author: Monday,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-13 13:55:21

Możesz użyć Path API w następujący sposób:

 var filenNme = Path.GetFileNameWithoutExtension([File Path]);

Więcej informacji: ścieżka.GetFileNameWithoutExtension

 27
Author: Peyman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-01 07:29:52
var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path);

/ align = "left" / GetFileNameWithoutExtension

 19
Author: Holystream,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-03 02:50:22

Spróbuj tego:

string fileName = Path.GetFileNameWithoutExtension(@"C:\Program Files\hello.txt");

Zwróci "hello" dla nazwy pliku.

 11
Author: Tim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-03 02:49:49
string Location = "C:\\Program Files\\hello.txt";

string FileName = Location.Substring(Location.LastIndexOf('\\') +
    1);
 9
Author: raman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-27 13:25:51

Spróbuj tego,

string FilePath=@"C:\mydir\myfile.ext";
string Result=Path.GetFileName(FilePath);//With Extension
string Result=Path.GetFileNameWithoutExtension(FilePath);//Without Extension
 6
Author: Sumanth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-27 07:37:02

Po pierwsze, kod w pytaniu nie daje opisanego wyjścia. Wyciąga rozszerzenie pliku ("txt") a nie nazwa pliku ("hello"). Aby to zrobić, ostatnia linia powinna wywołać First(), a nie Last(), w ten sposób...

static string GetFileBaseNameUsingSplit(string path)
{
    string[] pathArr = path.Split('\\');
    string[] fileArr = pathArr.Last().Split('.');
    string fileBaseName = fileArr.First().ToString();

    return fileBaseName;
}

Po dokonaniu tej zmiany, jedną rzeczą, o której należy pomyśleć, jeśli chodzi o ulepszenie tego kodu, jest ilość śmieci, które tworzy: {159]}

  • a string[] zawierający po jednym string dla każdego segmentu ścieżki w path
  • A string[] zawiera co najmniej jeden string dla każdego . w ostatnim segmencie ścieżki w path

Dlatego wyodrębnienie nazwy pliku bazowego ze ścieżki próbki "C:\Program Files\hello.txt" powinno wytworzyć (tymczasowy) objects "C:", "Program Files", "hello.txt", "hello", "txt", a string[3] i a string[2]. Może to być istotne, jeśli metoda jest wywoływana na dużej liczbie ścieżek. Aby to poprawić, możemy przeszukać path sami, aby zlokalizować punkty początkowe i końcowe nazwy bazy i użyć ich do utworzenia jednego Nowy string...

static string GetFileBaseNameUsingSubstringUnsafe(string path)
{
    // Fails on paths with no file extension - DO NOT USE!!
    int startIndex = path.LastIndexOf('\\') + 1;
    int endIndex = path.IndexOf('.', startIndex);
    string fileBaseName = path.Substring(startIndex, endIndex - startIndex);

    return fileBaseName;
}

To użycie indeksu znaku po ostatnim \ jako początku nazwy bazowej, a stamtąd szukanie pierwszego . do użycia jako indeksu znaku po końcu nazwy bazowej. Jest krótszy od oryginalnego kodu? Niezupełnie. Czy jest to" mądrzejsze " rozwiązanie? Tak myślę. Przynajmniej tak by było, gdyby nie fakt, że...

Jak widać z komentarza, poprzednia metoda jest problematyczna. Chociaż to działa, jeśli Załóżmy, że wszystkie ścieżki kończą się nazwą pliku z rozszerzeniem, to rzuci wyjątek, jeśli ścieżka kończy się \ (tzn. ścieżką do katalogu) lub w inny sposób nie zawiera rozszerzenia w ostatnim segmencie. Aby to naprawić, musimy dodać dodatkowe sprawdzenie do konta, gdy endIndex jest -1 (tzn. . nie został znaleziony)...

static string GetFileBaseNameUsingSubstring(string path)
{
    int startIndex = path.LastIndexOf('\\') + 1;
    int endIndex = path.IndexOf('.', startIndex);
    int length = (endIndex >= 0 ? endIndex : path.Length) - startIndex;
    string fileBaseName = path.Substring(startIndex, length);

    return fileBaseName;
}

Teraz ta wersja nie jest krótsza niż oryginał, ale jest bardziej wydajna i (teraz) poprawna.

Jeśli chodzi o metody. NET, które implementują to funkcjonalności, wiele innych odpowiedzi sugeruje użycie Path.GetFileNameWithoutExtension(), co jest oczywistym, łatwym rozwiązaniem, ale nie daje takich samych wyników jak kod w pytaniu. Istnieje subtelna, ale ważna różnica między GetFileBaseNameUsingSplit() aPath.GetFileNameWithoutExtension() (GetFileBaseNameUsingPath() poniżej): pierwszy wyciąga wszystko przed pierwszy . i ten ostatni wyciąga wszystko przed ostatni .. To nie robi różnicy dla próbki path w pytaniu, ale weź spójrz na tabelę porównującą wyniki powyższych czterech metod wywołanych różnymi ścieżkami...

opis metoda ścieżka wynik
pojedyncze rozszerzenie GetFileBaseNameUsingSplit() "C:\Program Files\hello.txt" "hello"
pojedyncze rozszerzenie GetFileBaseNameUsingPath() "C:\Program Files\hello.txt" "hello"
Single rozszerzenie GetFileBaseNameUsingSubstringUnsafe() "C:\Program Files\hello.txt" "hello"
pojedyncze rozszerzenie GetFileBaseNameUsingSubstring() "C:\Program Files\hello.txt" "hello"




Podwójne rozszerzenie GetFileBaseNameUsingSplit() "C:\Program Files\hello.txt.ext" "hello"
Double rozszerzenie GetFileBaseNameUsingPath() "C:\Program Files\hello.txt.ext" "hello.txt"
Podwójne rozszerzenie GetFileBaseNameUsingSubstringUnsafe() "C:\Program Files\hello.txt.ext" "hello"
Podwójne rozszerzenie GetFileBaseNameUsingSubstring() "C:\Program Files\hello.txt.ext" "hello"




Nie rozszerzenie GetFileBaseNameUsingSplit() "C:\Program Files\hello" "hello"
brak rozszerzenia GetFileBaseNameUsingPath() "C:\Program Files\hello" "hello"
brak rozszerzenia GetFileBaseNameUsingSubstringUnsafe() "C:\Program Files\hello" wyjątek: długość nie może być mniejsza niż zero. (Parametr "długość')
Nie rozszerzenie GetFileBaseNameUsingSubstring() "C:\Program Files\hello" "hello"




okres wiodący GetFileBaseNameUsingSplit() "C:\Program Files\.hello.txt" ""
okres wiodący GetFileBaseNameUsingPath() "C:\Program Files\.hello.txt" ".hello"
prowadzenie okres GetFileBaseNameUsingSubstringUnsafe() "C:\Program Files\.hello.txt" ""
okres wiodący GetFileBaseNameUsingSubstring() "C:\Program Files\.hello.txt" ""




okres schyłkowy GetFileBaseNameUsingSplit() "C:\Program Files\hello.txt." "hello"
Trailing okres GetFileBaseNameUsingPath() "C:\Program Files\hello.txt." "hello.txt"
okres schyłkowy GetFileBaseNameUsingSubstringUnsafe() "C:\Program Files\hello.txt." "hello"
okres schyłkowy GetFileBaseNameUsingSubstring() "C:\Program Files\hello.txt." "hello"




Katalog ścieżka GetFileBaseNameUsingSplit() "C:\Program Files\" ""
ścieżka katalogu GetFileBaseNameUsingPath() "C:\Program Files\" ""
ścieżka katalogu GetFileBaseNameUsingSubstringUnsafe() "C:\Program Files\" wyjątek: długość nie może być mniejsza niż zero. (Parametr "długość')
Katalog ścieżka GetFileBaseNameUsingSubstring() "C:\Program Files\" ""




bieżąca ścieżka do pliku GetFileBaseNameUsingSplit() "hello.txt" "hello"
bieżąca ścieżka do pliku GetFileBaseNameUsingPath() "hello.txt" "hello"
bieżący plik ścieżka GetFileBaseNameUsingSubstringUnsafe() "hello.txt" "hello"
bieżąca ścieżka do pliku GetFileBaseNameUsingSubstring() "hello.txt" "hello"




ścieżka do pliku nadrzędnego GetFileBaseNameUsingSplit() "..\hello.txt" "hello"
plik nadrzędny ścieżka GetFileBaseNameUsingPath() "..\hello.txt" "hello"
ścieżka do pliku nadrzędnego GetFileBaseNameUsingSubstringUnsafe() "..\hello.txt" "hello"
ścieżka do pliku nadrzędnego GetFileBaseNameUsingSubstring() "..\hello.txt" "hello"




Katalog nadrzędny ścieżka GetFileBaseNameUsingSplit() ".." ""
ścieżka katalogu nadrzędnego GetFileBaseNameUsingPath() ".." "."
ścieżka katalogu nadrzędnego GetFileBaseNameUsingSubstringUnsafe() ".." ""
ścieżka katalogu nadrzędnego GetFileBaseNameUsingSubstring() ".." ""

...i zobaczysz, że Path.GetFileNameWithoutExtension() różne wyniki po przekazaniu ścieżki, w której nazwa pliku ma podwójne rozszerzenie lub początek i/lub koniec .. Możesz wypróbować go samodzielnie za pomocą poniższego kodu...

using System;
using System.IO;
using System.Linq;
using System.Reflection;

namespace SO6921105
{
    internal class PathExtractionResult
    {
        public string Description { get; set; }
        public string Method { get; set; }
        public string Path { get; set; }
        public string Result { get; set; }
    }

    public static class Program
    {
        private static string GetFileBaseNameUsingSplit(string path)
        {
            string[] pathArr = path.Split('\\');
            string[] fileArr = pathArr.Last().Split('.');
            string fileBaseName = fileArr.First().ToString();

            return fileBaseName;
        }

        private static string GetFileBaseNameUsingPath(string path)
        {
            return Path.GetFileNameWithoutExtension(path);
        }

        private static string GetFileBaseNameUsingSubstringUnsafe(string path)
        {
            // Fails on paths with no file extension - DO NOT USE!!
            int startIndex = path.LastIndexOf('\\') + 1;
            int endIndex = path.IndexOf('.', startIndex);
            string fileBaseName = path.Substring(startIndex, endIndex - startIndex);

            return fileBaseName;
        }

        private static string GetFileBaseNameUsingSubstring(string path)
        {
            int startIndex = path.LastIndexOf('\\') + 1;
            int endIndex = path.IndexOf('.', startIndex);
            int length = (endIndex >= 0 ? endIndex : path.Length) - startIndex;
            string fileBaseName = path.Substring(startIndex, length);

            return fileBaseName;
        }

        public static void Main()
        {
            MethodInfo[] testMethods = typeof(Program).GetMethods(BindingFlags.NonPublic | BindingFlags.Static)
                .Where(method => method.Name.StartsWith("GetFileBaseName"))
                .ToArray();
            var inputs = new[] {
                new { Description = "Single extension",      Path = @"C:\Program Files\hello.txt"     },
                new { Description = "Double extension",      Path = @"C:\Program Files\hello.txt.ext" },
                new { Description = "No extension",          Path = @"C:\Program Files\hello"         },
                new { Description = "Leading period",        Path = @"C:\Program Files\.hello.txt"    },
                new { Description = "Trailing period",       Path = @"C:\Program Files\hello.txt."    },
                new { Description = "Directory path",        Path = @"C:\Program Files\"              },
                new { Description = "Current file path",     Path = "hello.txt"                       },
                new { Description = "Parent file path",      Path = @"..\hello.txt"                   },
                new { Description = "Parent directory path", Path = ".."                              }
            };
            PathExtractionResult[] results = inputs
                .SelectMany(
                    input => testMethods.Select(
                        method => {
                            string result;

                            try
                            {
                                string returnValue = (string) method.Invoke(null, new object[] { input.Path });

                                result = $"\"{returnValue}\"";
                            }
                            catch (Exception ex)
                            {
                                if (ex is TargetInvocationException)
                                    ex = ex.InnerException;
                                result = $"EXCEPTION: {ex.Message}";
                            }

                            return new PathExtractionResult() {
                                Description = input.Description,
                                Method = $"{method.Name}()",
                                Path = $"\"{input.Path}\"",
                                Result = result
                            };
                        }
                    )
                ).ToArray();
            const int ColumnPadding = 2;
            ResultWriter writer = new ResultWriter(Console.Out) {
                DescriptionColumnWidth = results.Max(output => output.Description.Length) + ColumnPadding,
                MethodColumnWidth = results.Max(output => output.Method.Length) + ColumnPadding,
                PathColumnWidth = results.Max(output => output.Path.Length) + ColumnPadding,
                ResultColumnWidth = results.Max(output => output.Result.Length) + ColumnPadding,
                ItemLeftPadding = " ",
                ItemRightPadding = " "
            };
            PathExtractionResult header = new PathExtractionResult() {
                Description = nameof(PathExtractionResult.Description),
                Method = nameof(PathExtractionResult.Method),
                Path = nameof(PathExtractionResult.Path),
                Result = nameof(PathExtractionResult.Result)
            };

            writer.WriteResult(header);
            writer.WriteDivider();
            foreach (IGrouping<string, PathExtractionResult> resultGroup in results.GroupBy(result => result.Description))
            {
                foreach (PathExtractionResult result in resultGroup)
                    writer.WriteResult(result);
                writer.WriteDivider();
            }
        }
    }

    internal class ResultWriter
    {
        private const char DividerChar = '-';
        private const char SeparatorChar = '|';

        private TextWriter Writer { get; }

        public ResultWriter(TextWriter writer)
        {
            Writer = writer ?? throw new ArgumentNullException(nameof(writer));
        }

        public int DescriptionColumnWidth { get; set; }

        public int MethodColumnWidth { get; set; }

        public int PathColumnWidth { get; set; }

        public int ResultColumnWidth { get; set; }

        public string ItemLeftPadding { get; set; }

        public string ItemRightPadding { get; set; }

        public void WriteResult(PathExtractionResult result)
        {
            WriteLine(
                $"{ItemLeftPadding}{result.Description}{ItemRightPadding}",
                $"{ItemLeftPadding}{result.Method}{ItemRightPadding}",
                $"{ItemLeftPadding}{result.Path}{ItemRightPadding}",
                $"{ItemLeftPadding}{result.Result}{ItemRightPadding}"
            );
        }

        public void WriteDivider()
        {
            WriteLine(
                new string(DividerChar, DescriptionColumnWidth),
                new string(DividerChar, MethodColumnWidth),
                new string(DividerChar, PathColumnWidth),
                new string(DividerChar, ResultColumnWidth)
            );
        }

        private void WriteLine(string description, string method, string path, string result)
        {
            Writer.Write(SeparatorChar);
            Writer.Write(description.PadRight(DescriptionColumnWidth));
            Writer.Write(SeparatorChar);
            Writer.Write(method.PadRight(MethodColumnWidth));
            Writer.Write(SeparatorChar);
            Writer.Write(path.PadRight(PathColumnWidth));
            Writer.Write(SeparatorChar);
            Writer.Write(result.PadRight(ResultColumnWidth));
            Writer.WriteLine(SeparatorChar);
        }
    }
}

TL; DR kod w pytaniu nie zachowuje się tak, jak wielu wydaje się oczekiwać w niektórych narożnych przypadkach. Jeśli masz zamiar napisać własny kod manipulacji ścieżką, pamiętaj, aby wziąć pod uwagę...

  • ...jak zdefiniować "nazwę pliku bez rozszerzenia" (czy to wszystko przed pierwszym . czy wszystko przed ostatnim .?)
  • ...pliki z wieloma rozszerzeniami
  • ...Pliki bez rozszerzenia
  • ...pliki z wiodącym .
  • ...pliki z zakończeniem . (prawdopodobnie nie coś, co kiedykolwiek spotkasz w systemie Windows, ale są możliwe )
  • ...katalogi z "rozszerzeniem" lub które w inny sposób zawierają .
  • ...ścieżki, które kończą się \
  • ...względny ścieżki

Nie wszystkie ścieżki plików są zgodne ze standardowym wzorem X:\Directory\File.ext!

 4
Author: Lance U. Matthews,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-01-29 03:34:26
string filepath = "C:\\Program Files\\example.txt";
FileVersionInfo myFileVersionInfo = FileVersionInfo.GetVersionInfo(filepath);
FileInfo fi = new FileInfo(filepath);
Console.WriteLine(fi.Name);

//input to the "fi" is a full path to the file from "filepath"
//This code will return the fileName from the given path

//output
//example.txt
 1
Author: Ganesh Kalidas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-01-31 07:08:44
Namespace: using System.IO;  
 //use this to get file name dynamically 
 string filelocation = Properties.Settings.Default.Filelocation;
//use this to get file name statically 
//string filelocation = @"D:\FileDirectory\";
string[] filesname = Directory.GetFiles(filelocation); //for multiple files

Your path configuration in App.config file if you are going to get file name dynamically  -

    <userSettings>
        <ConsoleApplication13.Properties.Settings>
          <setting name="Filelocation" serializeAs="String">
            <value>D:\\DeleteFileTest</value>
          </setting>
              </ConsoleApplication13.Properties.Settings>
      </userSettings>
 0
Author: Ritesh Yadav,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-10 13:22:18