需要根据业务实体的名称在其中创建文件夹和子文件夹,使名称尽可能接近原始名称。
同时还要考虑到操作系统的限制,这就增加了麻烦——如果linux-macos实际上没有限制,那么在windows上就绰绰有余了。
结果是这段代码,无效字符被替换为点。
private static readonly string NormalizationPattern = string.Format(@"([{0}]*\.+$)|([{0}]+)", Regex.Escape(string.Concat(new string(Path.GetInvalidPathChars()), "?", "/", "*", "\"")));
private static readonly string[] DosReservedNames = { "CON", "PRN", "AUX", "NUL", "COM0", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT0", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" };
public static string NormalizePath(string name)
{
if (Environment.OSVersion.Platform == PlatformID.Unix ||
Environment.OSVersion.Platform == PlatformID.MacOSX)
return name;
const string replacement = ".";
var matchesCount = Regex.Matches(name, @":\\").Count;
string correctName;
if (matchesCount > 0)
{
var regex = new Regex(@":", RegexOptions.RightToLeft);
correctName = regex.Replace(name, replacement, regex.Matches(name).Count - matchesCount);
}
else
correctName = name.Replace(":", replacement);
var replace = Regex.Replace(correctName, NormalizationPattern, replacement);
foreach (var reservedName in DosReservedNames)
{
var builder = new List<string>();
foreach (var folder in replace.Split(Path.DirectorySeparatorChar))
{
var changedName = folder;
if (string.Equals(folder, reservedName, StringComparison.InvariantCultureIgnoreCase))
changedName = replacement + reservedName;
var value = reservedName + '.';
if (folder.StartsWith(value, StringComparison.InvariantCultureIgnoreCase))
changedName = replacement + value + folder.Remove(0, value.Length);
builder.Add(changedName);
}
replace = string.Join<string>(Path.DirectorySeparatorChar.ToString(), builder);
}
return replace.TrimEnd(' ', '.');
}
文件夹根目录通常由系统选择并且已经存在。然后通过规范化创建所有嵌套级别。因此,例如,点和空格的修剪只在最后完成,而不是在每个级别。也许你不应该这样做,你应该修剪每个文件夹的名称。
为所有这些编写测试,整个案例如下所示:
[Test, Sequential]
public void CheckNotAllowedNames([Values(
"test"
,@"C:\somename\somename:name"
,@"usr\home\somename:name"
,@"start < > : "" / \ | ? * end"
,"\x15\x3D" // less than ASCII space
,"\x21\x3D" // HEX of !, valid
,"\x3F\x3D" // HEX of ?, not valid
,@"C:\somename\ trailing space "
,@"C:\somename\...trailing period..."
,@"C:\somename\CON"
,@"C:\somename\CON.txt"
,@"CON"
,@"C:\somename\con.txt\context"
,@"home\NUL.liza"
,@"home\ NUL.liza"
,@"C:\somename\..." // Bad name get the root folder, bug =_=
,@"root\..\sub"
,@"root\..\"
,@".\..\some?folder"
,@"root\.." // relative path trimmed, bug =_=
)] string name, [Values(
"test"
,@"C:\somename\somename.name"
,@"usr\home\somename.name"
,@"start . . . . . \ . . . end"
,".="
,"!="
,".="
,@"C:\somename\ trailing space"
,@"C:\somename\...trailing period"
,@"C:\somename\.CON"
,@"C:\somename\.CON.txt"
,@".CON"
,@"C:\somename\.CON.txt\context"
,@"home\.NUL.liza"
,@"home\ NUL.liza"
,@"C:\somename\"
,@"root\..\sub"
,@"root\..\"
,@".\..\some.folder"
,@"root\"
)] string expected)
{
Assert.AreEqual(expected, NormalizePath(name));
}
实际上,首先,我希望有人可以查看并找出我错过的错误。
其次 - 也许我正在重新发明轮子,哪里有现成的标准化?我用谷歌搜索了很久,但我可能会错过,周围有很多自行车。
UPD1:发现了相对路径的问题,我仍然不知道如何解决,我添加了当前行为的测试。dotnet API 允许您请求创建文件夹root\folder\.....并返回root. msdn上的帮助说可以通过api创建积分,但是不应该,以免造成问题。最后如何更正确地处理相对路径是另一个问题。
因此,我明确分离了使用设置中指定的根文件夹和业务对象文件夹的逻辑。
设置文件夹的验证很简单:
并且从业务对象验证文件夹实际上与问题中的相同,但结果却大大简化了:
当前的实现需要调用
RemoveInvalidCharsFromName每个嵌套级别。那些。如果逻辑需要 Folder1/Subfolder2/Subfolder3,那么您需要分别“规范化”每个文件夹: