C#按名称中的自然数排序对文件进行排序?
我有像这样的目录中的文件
0-0.jpeg 0-1.jpeg 0-5.jpeg 0-9.jpeg 0-10.jpeg 0-12.jpeg
….
当我加载文件时:
FileInfo[] files = di.GetFiles();
他们的订单错误(他们应该像上面那样):
0-0.jpeg 0-1.jpeg 0-10.jpeg 0-12.jpeg 0-5.jpeg 0-9.jpeg
如何解决?
我试图对它们进行排序,但没办法:
1) Array.Sort(files, (f1, f2) => f1.Name.CompareTo(f2.Name)); 2) Array.Sort(files, (x, y) => StringComparer.OrdinalIgnoreCase.Compare(x.Name, y.Name));
按字母顺序,“错误”的顺序实际上是正确的。 如果你想要按数字排序,那么你需要:
- 将文件名转换为数字列表并对其进行排序
- 以字母和数字排序相同的方式命名文件(0-001.jpeg和0-030.jpg)
- 依赖文件创建时间进行排序(假设文件是按顺序创建的)。
有关#3的示例,请参阅排序Directory.GetFiles()的答案。
请在此处查看“CustomSort”function。
List list = new List () { "0-5.jpeg", "0-9.jpeg", "0-0.jpeg", "0-1.jpeg", "0-10.jpeg", "0-12.jpeg"}; list.CustomSort().ToList().ForEach(x => Console.WriteLine(x));
它的输出:
0-0.jpeg 0-1.jpeg 0-5.jpeg 0-9.jpeg 0-10.jpeg 0-12.jpeg
要解决此问题,您可以使用StrCmpLogicalW Windows API。
有关详细信息,请参阅此Artice 。
我知道这可能会迟到,但这是另一个完美的解决方案
FileInfo[] files = di.GetFiles().OrderBy(n => Regex.Replace(n.Name, @"\d+", n => n.Value.PadLeft(4, '0')));
在OrderBy Clause
使用Regex替换:
Regex.Replace(n.Name, @"\d+", n => n.Value.PadLeft(4, '0'))
这样做,它pads
文件名中的数值,每个数字的长度为4个字符:
0-0.jpeg -> 0000-0000.jpeg 0-1.jpeg -> 0000-0001.jpeg 0-5.jpeg -> 0000-0005.jpeg 0-9.jpeg -> 0000-0009.jpeg 0-10.jpeg -> 0000-0010.jpeg 0-12.jpeg -> 0000-0012.jpeg
但这只发生在OrderBy
子句中,它不会以任何方式触及原始文件名。 您最终将在数组中的顺序是“人类自然”顺序。
您的文件名似乎是结构化的。 如果您只是对它们进行排序,它们就像普通的字符 你需要:
- 将文件名解析为其组成部分。
- 将数字段转换为数字值。
- 以所需顺序比较该结构以获得预期的归类序列。
就个人而言,我创建了一个表示文件名中隐含的结构的类。 也许它应该包装FileInfo
。 该类的构造函数应将文件名解析为其组成部分,并适当地实例化该类的属性。
该类应该实现IComparable
/ IComparable
(或者您可以创建Comparer的实现)。
对对象进行排序,然后它们应按照您想要的整理顺序出现。
如果您的文件名看起来像由3部分组成:
- 一个高阶数值(我们称之为’hi’),
- 一个低阶数值(让我们称之为’lo’),
- 和一个扩展(让我们称之为’ext’)
所以你的课可能看起来像
public class MyFileInfoWrapper : IComparable,IComparable { public MyFileInfoWrapper( FileInfo fi ) { // your implementation here throw new NotImplementedException() ; } public int Hi { get ; private set ; } public int Lo { get ; private set ; } public string Extension { get ; private set ; } public FileInfo FileInfo { get ; private set ; } public int CompareTo( MyFileInfoWrapper other ) { int cc ; if ( other == null ) cc = -1 ; else if ( this.Hi < other.Hi ) cc = -1 ; else if ( this.Hi > other.Hi ) cc = +1 ; else if ( this.Lo < other.Lo ) cc = -1 ; else if ( this.Lo > other.Lo ) cc = +1 ; else cc = string.Compare( this.Extension , other.Extension , StringComparison.InvariantCultureIgnoreCase ) ; return cc ; } public int CompareTo( object obj ) { int cc ; if ( obj == null ) cc = -1 ; else if ( obj is MyFileInfoWrapper ) cc = CompareTo( (MyFileInfoWrapper) obj ) ; else throw new ArgumentException("'obj' is not a 'MyFileInfoWrapper' type.", "obj") ; return cc ; } }