IComparer 问题 + 如何在 .NET 中自然地对字符串数组进行排序 (FILE_10 > FILE_2)?
在我的帖子底部解决了。
或者更具体地说:
我有一堆 FileInfo 对象(我需要 FileInfo 对象来排除隐藏、系统和重新分析点文件)。
我需要根据 FileInfo.FullName 对 FileInfo[] 进行自然排序。 因此 FILE_10.ext 应该位于 FILE_2.ext 之后。 幸运的是,FileInfo[] 只包含一种扩展名的文件。
我已经实现了一个比较器:
/// <summary>
/// Compares FileInfo objects based on the files full path.
/// This comparer is flawed in that it will only work correctly
/// on files with the same extension.
/// Though that could easily be fixed.
/// </summary>
private class FileInfoSorter : IComparer
{
int IComparer.Compare(Object x, Object y)
{
FileInfo _x = x as FileInfo;
FileInfo _y = y as FileInfo;
// FYI:
//ExprFileVersion = new Regex("(.*)_([0-9]+)\\.[^\\.]+$", RegexOptions.Compiled);
Match m1 = RegExps.ExprFileVersion.Match(_x.FullName);
Match m2 = RegExps.ExprFileVersion.Match(_y.FullName);
if (m1.Success && m2.Success) // we have versioned files
{
int n1;
int n2;
try
{
n1 = int.Parse(m1.Groups[2].Value);
}
catch (OverflowException ex)
{
// Don't know if this works.
ex.Data["File"] = _x.FullName;
throw;
}
try
{
n2 = int.Parse(m2.Groups[2].Value);
}
catch (OverflowException ex)
{
// Don't know if this works.
ex.Data["File"] = _y.FullName;
throw;
}
string s1 = m1.Groups[1].Value;
string s2 = m2.Groups[1].Value;
if (s1.Equals(s2))
{
return n1.CompareTo(n2); // compare numbers naturally. E.g. 11 > 6
}
else // not the same base file name. So the version does not matter.
{
return ((new CaseInsensitiveComparer()).Compare(_x.FullName, _y.FullName));
}
}
else // not versioned
{
return ((new CaseInsensitiveComparer()).Compare(_x.FullName, _y.FullName));
}
}
}
现在出现的问题是 int.Parse 抛出一个 OverflowException,我无法在正确的位置捕获它(由于某种原因它在 return 语句行上重复出现,我无法在一级智能地处理它)更进一步,因为它永远不会到达那里)。
问题是:是否有针对这种事情的预先实现的比较器? 异常出现在有趣的地方的原因可能是什么?
调用代码:
IComparer fiComparer = new FileInfoSorter();
try
{
Array.Sort(filesOfExtInfo, fiComparer);
}
catch (OverflowException ex)
{
// Do not know yet if I can use ex.Data in this way.
WriteStatusLineAsync("Error: Encountered too large a version number on file: " + ex.Data["File"]);
}
EDIT1:Int.Parse遇到太大的数字时会抛出OverflowException。 它不应该定期发生,但我希望它被覆盖。
EDIT2:我最终调整了自己的比较器。 远离 int.Parse 并在左侧填充零以进行比较。 代码在这里:
public class FileInfoSorter : IComparer
{
int IComparer.Compare(Object x, Object y)
{
FileInfo _x = x as FileInfo;
FileInfo _y = y as FileInfo;
Match m1 = RegExps.ExprFileVersion.Match(_x.FullName);
Match m2 = RegExps.ExprFileVersion.Match(_y.FullName);
if (m1.Success && m2.Success) // we have versioned files
{
string n1;
string n2;
n1 = m1.Groups[2].Value;
n2 = m2.Groups[2].Value;
string s1 = m1.Groups[1].Value;
string s2 = m2.Groups[1].Value;
int max = Math.Max(n1.Length, n2.Length);
n1 = n1.PadLeft(max, '0');
n2 = n2.PadLeft(max, '0');
if (s1.Equals(s2)) // we have to compare the version
// which is now left-padded with 0s.
{
return ((new CaseInsensitiveComparer()).Compare(n1, n2));
}
else // not the same base file name. So the version does not matter.
{
return ((new CaseInsensitiveComparer()).Compare(_x.FullName, _y.FullName));
}
}
else // not versioned
{
return ((new CaseInsensitiveComparer()).Compare(_x.FullName, _y.FullName));
}
}
}
SOLVED at the bottom of my post.
Or more specifically:
I have a bunch of FileInfo objects (I need the FileInfo objects to exclude hidden, system and reparse point files).
I need to sort FileInfo[] naturally based on their FileInfo.FullName. So FILE_10.ext should come after FILE_2.ext. Luckily the FileInfo[] contains files of only one extension.
I have implemented a comparer:
/// <summary>
/// Compares FileInfo objects based on the files full path.
/// This comparer is flawed in that it will only work correctly
/// on files with the same extension.
/// Though that could easily be fixed.
/// </summary>
private class FileInfoSorter : IComparer
{
int IComparer.Compare(Object x, Object y)
{
FileInfo _x = x as FileInfo;
FileInfo _y = y as FileInfo;
// FYI:
//ExprFileVersion = new Regex("(.*)_([0-9]+)\\.[^\\.]+$", RegexOptions.Compiled);
Match m1 = RegExps.ExprFileVersion.Match(_x.FullName);
Match m2 = RegExps.ExprFileVersion.Match(_y.FullName);
if (m1.Success && m2.Success) // we have versioned files
{
int n1;
int n2;
try
{
n1 = int.Parse(m1.Groups[2].Value);
}
catch (OverflowException ex)
{
// Don't know if this works.
ex.Data["File"] = _x.FullName;
throw;
}
try
{
n2 = int.Parse(m2.Groups[2].Value);
}
catch (OverflowException ex)
{
// Don't know if this works.
ex.Data["File"] = _y.FullName;
throw;
}
string s1 = m1.Groups[1].Value;
string s2 = m2.Groups[1].Value;
if (s1.Equals(s2))
{
return n1.CompareTo(n2); // compare numbers naturally. E.g. 11 > 6
}
else // not the same base file name. So the version does not matter.
{
return ((new CaseInsensitiveComparer()).Compare(_x.FullName, _y.FullName));
}
}
else // not versioned
{
return ((new CaseInsensitiveComparer()).Compare(_x.FullName, _y.FullName));
}
}
}
Now the problem arises that int.Parse throws an OverflowException which I was not able to catch at the right point (it reoccurs on the line of the return statement for some reason and I can not handle it intelligently one level further up because it never arrives there).
The question is: Is there a pre-implemented comparer for this kind of thing? And what could be the reason that the exception turns up at funny places?
Calling code:
IComparer fiComparer = new FileInfoSorter();
try
{
Array.Sort(filesOfExtInfo, fiComparer);
}
catch (OverflowException ex)
{
// Do not know yet if I can use ex.Data in this way.
WriteStatusLineAsync("Error: Encountered too large a version number on file: " + ex.Data["File"]);
}
EDIT1: Int.Parse throws OverflowException when it encounters a too big number. It should not happen on a regular basis but I want it covered.
EDIT2: I ended up adjusting my own Comparer. Went away from int.Parse and just left-padded with zeroes for comparison.
Code here:
public class FileInfoSorter : IComparer
{
int IComparer.Compare(Object x, Object y)
{
FileInfo _x = x as FileInfo;
FileInfo _y = y as FileInfo;
Match m1 = RegExps.ExprFileVersion.Match(_x.FullName);
Match m2 = RegExps.ExprFileVersion.Match(_y.FullName);
if (m1.Success && m2.Success) // we have versioned files
{
string n1;
string n2;
n1 = m1.Groups[2].Value;
n2 = m2.Groups[2].Value;
string s1 = m1.Groups[1].Value;
string s2 = m2.Groups[1].Value;
int max = Math.Max(n1.Length, n2.Length);
n1 = n1.PadLeft(max, '0');
n2 = n2.PadLeft(max, '0');
if (s1.Equals(s2)) // we have to compare the version
// which is now left-padded with 0s.
{
return ((new CaseInsensitiveComparer()).Compare(n1, n2));
}
else // not the same base file name. So the version does not matter.
{
return ((new CaseInsensitiveComparer()).Compare(_x.FullName, _y.FullName));
}
}
else // not versioned
{
return ((new CaseInsensitiveComparer()).Compare(_x.FullName, _y.FullName));
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
就在这里。 这个问题已经在此处得到了解答。 您基本上想通过调用 StrCmpLogicalW 函数P/Invoke 层。 有关完整详细信息,请参阅原始答案:
C# 中的自然排序顺序
Yes, there is. This question has already been answered here. You basically want to call the StrCmpLogicalW function through the P/Invoke layer. See the original answer for full details:
Natural Sort Order in C#