По-перше, код у питанні не дає описаного результату. Він витягує розширення файлу ( "txt"
), а не ім'я бази файлів ( "hello"
). Для цього слід зателефонувати останній рядок First()
, а не Last()
так ...
static string GetFileBaseNameUsingSplit(string path)
{
string[] pathArr = path.Split('\\');
string[] fileArr = pathArr.Last().Split('.');
string fileBaseName = fileArr.First().ToString();
return fileBaseName;
}
Зробивши цю зміну, варто подумати над тим, щоб покращити цей код - кількість сміття, яке він створює:
string[]
, Що містять один string
для кожного сегмента шляху вpath
string[]
Що містить , щонайменше , один string
для кожного .
сегмента шляху в минуломуpath
Таким чином, витяг імені базового файлу з шляху зразка "C:\Program Files\hello.txt"
має виробляти (тимчасовий) object
з "C:"
, "Program Files"
, "hello.txt"
, "hello"
, "txt"
, A string[3]
, і string[2]
. Це може бути суттєвим, якщо метод викликається на великій кількості шляхів. Щоб покращити це, ми можемо шукати path
себе, щоб знайти початкову та кінцеву точки базового імені та використовувати їх для створення одного нового 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;
}
Для цього використовується індекс символу після останнього \
як початок базового імені, і звідти шукають першого, який .
буде використовувати як індекс символу після закінчення базового імені. Це коротше, ніж початковий код? Не зовсім. Це "розумніше" рішення? Я думаю так. Принаймні, якби не той факт, що ...
Як видно з коментаря, попередній метод є проблематичним. Хоча це працює, якщо ви припускаєте, що всі шляхи закінчуються іменем файлу з розширенням, він викине виняток, якщо шлях закінчується \
(тобто шлях до каталогу) або іншим чином не містить розширення в останньому сегменті. Щоб виправити це, нам потрібно додати додатковий чек для обліку, коли endIndex
є -1
(тобто .
не знайдено) ...
static string GetFileBaseNameUsingSubstringSafe(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;
}
Зараз ця версія ніколи не коротша за оригінал, але вона є і більш ефективною і (зараз) правильною.
Що стосується методів .NET, які реалізують цю функціональність, багато інших відповідей пропонують використовувати Path.GetFileNameWithoutExtension()
, що є очевидним, простим рішенням, але не дає тих же результатів , що і код у питанні. Існує тонка, але важлива різниця між GetFileBaseNameUsingSplit()
і Path.GetFileNameWithoutExtension()
( GetFileBaseNameUsingPath()
нижче): перший витягує все до першого, .
а другий вилучає все до останнього .
. Це не має значення для вибірки path
у запитанні, але погляньте на цю таблицю, порівнюючи результати вищезгаданих чотирьох методів, коли їх викликають різними шляхами ...
| Description | Method | Path | Result |
|-----------------------|---------------------------------------|----------------------------------|------------------------------------------------------------------|
| Single extension | GetFileBaseNameUsingSplit() | "C:\Program Files\hello.txt" | "hello" |
| Single extension | GetFileBaseNameUsingPath() | "C:\Program Files\hello.txt" | "hello" |
| Single extension | GetFileBaseNameUsingSubstringUnsafe() | "C:\Program Files\hello.txt" | "hello" |
| Single extension | GetFileBaseNameUsingSubstringSafe() | "C:\Program Files\hello.txt" | "hello" |
|-----------------------|---------------------------------------|----------------------------------|------------------------------------------------------------------|
| Double extension | GetFileBaseNameUsingSplit() | "C:\Program Files\hello.txt.ext" | "hello" |
| Double extension | GetFileBaseNameUsingPath() | "C:\Program Files\hello.txt.ext" | "hello.txt" |
| Double extension | GetFileBaseNameUsingSubstringUnsafe() | "C:\Program Files\hello.txt.ext" | "hello" |
| Double extension | GetFileBaseNameUsingSubstringSafe() | "C:\Program Files\hello.txt.ext" | "hello" |
|-----------------------|---------------------------------------|----------------------------------|------------------------------------------------------------------|
| No extension | GetFileBaseNameUsingSplit() | "C:\Program Files\hello" | "hello" |
| No extension | GetFileBaseNameUsingPath() | "C:\Program Files\hello" | "hello" |
| No extension | GetFileBaseNameUsingSubstringUnsafe() | "C:\Program Files\hello" | EXCEPTION: Length cannot be less than zero. (Parameter 'length') |
| No extension | GetFileBaseNameUsingSubstringSafe() | "C:\Program Files\hello" | "hello" |
|-----------------------|---------------------------------------|----------------------------------|------------------------------------------------------------------|
| Leading period | GetFileBaseNameUsingSplit() | "C:\Program Files\.hello.txt" | "" |
| Leading period | GetFileBaseNameUsingPath() | "C:\Program Files\.hello.txt" | ".hello" |
| Leading period | GetFileBaseNameUsingSubstringUnsafe() | "C:\Program Files\.hello.txt" | "" |
| Leading period | GetFileBaseNameUsingSubstringSafe() | "C:\Program Files\.hello.txt" | "" |
|-----------------------|---------------------------------------|----------------------------------|------------------------------------------------------------------|
| Trailing period | GetFileBaseNameUsingSplit() | "C:\Program Files\hello.txt." | "hello" |
| Trailing period | GetFileBaseNameUsingPath() | "C:\Program Files\hello.txt." | "hello.txt" |
| Trailing period | GetFileBaseNameUsingSubstringUnsafe() | "C:\Program Files\hello.txt." | "hello" |
| Trailing period | GetFileBaseNameUsingSubstringSafe() | "C:\Program Files\hello.txt." | "hello" |
|-----------------------|---------------------------------------|----------------------------------|------------------------------------------------------------------|
| Directory path | GetFileBaseNameUsingSplit() | "C:\Program Files\" | "" |
| Directory path | GetFileBaseNameUsingPath() | "C:\Program Files\" | "" |
| Directory path | GetFileBaseNameUsingSubstringUnsafe() | "C:\Program Files\" | EXCEPTION: Length cannot be less than zero. (Parameter 'length') |
| Directory path | GetFileBaseNameUsingSubstringSafe() | "C:\Program Files\" | "" |
|-----------------------|---------------------------------------|----------------------------------|------------------------------------------------------------------|
| Current file path | GetFileBaseNameUsingSplit() | "hello.txt" | "hello" |
| Current file path | GetFileBaseNameUsingPath() | "hello.txt" | "hello" |
| Current file path | GetFileBaseNameUsingSubstringUnsafe() | "hello.txt" | "hello" |
| Current file path | GetFileBaseNameUsingSubstringSafe() | "hello.txt" | "hello" |
|-----------------------|---------------------------------------|----------------------------------|------------------------------------------------------------------|
| Parent file path | GetFileBaseNameUsingSplit() | "..\hello.txt" | "hello" |
| Parent file path | GetFileBaseNameUsingPath() | "..\hello.txt" | "hello" |
| Parent file path | GetFileBaseNameUsingSubstringUnsafe() | "..\hello.txt" | "hello" |
| Parent file path | GetFileBaseNameUsingSubstringSafe() | "..\hello.txt" | "hello" |
|-----------------------|---------------------------------------|----------------------------------|------------------------------------------------------------------|
| Parent directory path | GetFileBaseNameUsingSplit() | ".." | "" |
| Parent directory path | GetFileBaseNameUsingPath() | ".." | "." |
| Parent directory path | GetFileBaseNameUsingSubstringUnsafe() | ".." | "" |
| Parent directory path | GetFileBaseNameUsingSubstringSafe() | ".." | "" |
|-----------------------|---------------------------------------|----------------------------------|------------------------------------------------------------------|
... і ви побачите, що Path.GetFileNameWithoutExtension()
дає різні результати, коли пройшов шлях, коли ім'я файлу має подвійне розширення або провідний та / або трейлінг .
. Ви можете спробувати це за допомогою наступного коду ...
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 GetFileBaseNameUsingSubstringSafe(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 Код у питанні не так поводиться, як багато хто, здається, очікує в деяких кутових випадках. Якщо ви збираєтеся написати власний код маніпуляції шляхом, обов'язково врахуйте ...
- ... як ви визначаєте "розширення" (це все до першого
.
чи все до останнього .
?)
- ... файли з декількома розширеннями
- ... файли без розширення
- ... файли з ведучим
.
- ... файли із заднім числом
.
(можливо, це не те, що ви коли-небудь зустрінете в Windows, але вони можливі )
- ... каталоги з "розширенням" або які іншим чином містять
.
- ... шляхи, які закінчуються а
\
- ... відносні шляхи
Не всі шляхи до файлів відповідають звичайній формулі X:\Directory\File.ext
!
Path.GetFileName("C:\\dev\\some\\path\\to\\file.cs")
повертає той самий рядок і не перетворює його на "file.cs" чомусь. Якщо я копіюю / вставляю свій код в онлайн-компілятор (наприклад, rextester.com ), він працює ...?