İlk olarak, sorudaki kod açıklanan çıktıyı üretmez. Dosya taban adını ( ) değil , dosya uzantısını ( "txt"
) ayıklar . Bunu yapmak için son satırın çağırması gerekir , böyle değil ..."hello"
First()
Last()
static string GetFileBaseNameUsingSplit(string path)
{
string[] pathArr = path.Split('\\');
string[] fileArr = pathArr.Last().Split('.');
string fileBaseName = fileArr.First().ToString();
return fileBaseName;
}
Bu değişikliği yaptıktan sonra, bu kodu geliştirmek kadar düşünülmesi gereken bir şey, yarattığı çöp miktarıdır:
- A'daki her yol segmenti için bir tane
string[]
içerenstring
path
- A'daki son yol segmentinde her
string[]
biri string
için en az bir tane içeren A.
path
Bu nedenle, örnek yolu taban dosya adı ekstre "C:\Program Files\hello.txt"
üretmelidir (geçici) object
ler "C:"
, "Program Files"
, "hello.txt"
, "hello"
, "txt"
, bir string[3]
ve bir string[2]
. Yöntem çok sayıda yolda çağrıldığında bu önemli olabilir. Bunu iyileştirmek path
için, temel adın başlangıç ve bitiş noktalarını bulmak için kendimizi arayabilir ve bunları yeni bir tane oluşturmak için kullanabiliriz 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;
}
Bu, \
temel adın başlangıcı olarak sondan sonraki karakterin dizinini kullanır ve oradan .
temel adın sonundan sonra karakterin dizini olarak kullanılacak ilkini arar . Bu orijinal koddan daha mı kısa? Pek değil. "Daha akıllı" bir çözüm mü? Sanırım. En azından, olmasa bile ...
Yorumdan da görebileceğiniz gibi, önceki yöntem sorunludur. Tüm yolların bir uzantıya sahip bir dosya adıyla sona erdiğini varsayarsanız da, yol sonla bitiyorsa \
(yani bir dizin yolu) veya başka bir şekilde son segmentte uzantı içermiyorsa bir istisna atar . Bunu düzeltmek için, ne zaman endIndex
olduğunu -1
(yani .
bulunamadı) hesaba katmak için ekstra bir kontrol eklememiz gerekir ...
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;
}
Şimdi bu sürüm orijinalden daha kısa bir yerde değil, ancak daha verimli ve (şimdi) de doğru.
Bu işlevselliği uygulayan .NET yöntemleri ile ilgili olarak, diğer birçok yanıt Path.GetFileNameWithoutExtension()
, açık ve kolay bir çözümdür, ancak sorudaki kodla aynı sonuçları vermez . GetFileBaseNameUsingSplit()
Ve Path.GetFileNameWithoutExtension()
( GetFileBaseNameUsingPath()
aşağıda) arasında ince ama önemli bir fark vardır : ilki her şeyden önce ilkini .
çıkarır ve ikincisi her şeyi sondan önce çıkarır .
. Bu path
, sorudaki örnek için bir fark yaratmaz , ancak çeşitli yollarla çağrıldığında yukarıdaki dört yöntemin sonuçlarını karşılaştıran bu tabloya bakın ...
| 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() | ".." | "" |
|-----------------------|---------------------------------------|----------------------------------|------------------------------------------------------------------|
... ve Path.GetFileNameWithoutExtension()
dosya adının çift uzantıya veya öndeki ve / veya sondaki bir yola geçildiğinde farklı sonuçlar verdiğini göreceksiniz .
. Aşağıdaki kodla kendiniz deneyebilirsiniz ...
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 Söz konusu kod , bazı köşe vakalarında pek çok kişi beklendiği gibi davranmıyor . Kendi yol manipülasyon kodunuzu yazacaksanız, dikkate aldığınızdan emin olun ...
- ... bir "uzantıyı" nasıl tanımlarsınız (ilkinden önceki
.
her şey .
mi yoksa sondan önceki her şey mi?)
- ... birden fazla uzantıya sahip dosyalar
- ... uzantısı olmayan dosyalar
- ... önde gelen dosyalar
.
- ... sondaki dosyalar
.
(muhtemelen Windows'ta karşılaşacağınız bir şey değil, ancak bunlar mümkündür )
- ... "uzantılı" veya başka şekilde
.
- ... ile biten yollar
\
- ... göreli yollar
Tüm dosya yolları normal formülünü izlemez X:\Directory\File.ext
!
Path.GetFileName("C:\\dev\\some\\path\\to\\file.cs")
aynı dizeyi döndürüyor ve herhangi bir nedenle "file.cs" biçimine dönüştürmüyor. Kodumu bir çevrimiçi derleyiciye ( rextester.com gibi ) kopyalayıp yapıştırırsam çalışır ...?