ประการแรกรหัสในคำถามไม่ได้สร้างผลลัพธ์ที่อธิบายไว้ มันแยกนามสกุลไฟล์( "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
- A
string[]
ที่มีอย่างน้อยหนึ่งรายการstring
สำหรับแต่ละรายการ.
ในส่วนของเส้นทางสุดท้ายpath
ดังนั้นการแยกชื่อไฟล์ฐานจากเส้นทางตัวอย่าง"C:\Program Files\hello.txt"
ควรผลิต (ชั่วคราว) object
s "C:"
, "Program Files"
, "hello.txt"
, "hello"
, "txt"
เป็นและ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
( .
ไม่พบie ) ...
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 แต่เป็นไปได้ )
- ... ไดเรกทอรีที่มี "ส่วนขยาย" หรือที่ประกอบด้วย
.
- ... เส้นทางที่ลงท้ายด้วย a
\
- ... เส้นทางสัมพัทธ์
เส้นทางของไฟล์ไม่ครบทุกสูตรตามปกติX:\Directory\File.ext
!
Path.GetFileName("C:\\dev\\some\\path\\to\\file.cs")
คืนสตริงเดิมและไม่แปลงเป็น "file.cs" ด้วยเหตุผลบางอย่าง หากฉันคัดลอก / วางรหัสของฉันลงในคอมไพเลอร์ออนไลน์ (เช่นrextester.com ) มันใช้งานได้ ... ?