如何使用 C# 对数字子字符串上的字符串列表进行排序

How to order list of strings on number substring using C#

我有一个字符串列表,每个字符串都包含一个数字子字符串,我想根据该子字符串的数值对其进行重新排序。该集合看起来像这样,但更大:

List<string> strings= new List<string>
{
    "some-name-(1).jpg",
    "some-name-(5).jpg",
    "some-name-(5.1).jpg",
    "some-name-(6).jpg",
    "some-name-(12).jpg"
};

数字总是会被括号括起来,这是字符串中唯一的括号,所以使用String.IndexOf是靠谱的。请注意,不仅可能缺少数字,还可能存在小数,而不仅仅是整数。

我很难得到一个重新排序的列表,其中包含那些已根据该子字符串的数值排序的相同字符串。有没有人有办法做到这一点,希望表现良好?谢谢

这将检查括号之间的项目是否可转换为 double,如果不是,则在这种情况下将 return -1。

var numbers = strings.Select( x => x.Substring( x.IndexOf( "(" ) + 1, 
    x.IndexOf( ")" ) - x.IndexOf( "(" ) - 1 ) ).Select( x =>
{
   double val;
   if( double.TryParse( x, out val ) ) {
      return val;
   }

   // Or whatever you want to do
   return -1;
} ).OrderBy( x => x ); // Or use OrderByDescending

如果您确定括号之间总会有一个数字,则使用它,因为它更短:

var numbers = strings.Select( 
   x => x.Substring( x.IndexOf( "(" ) + 1, x.IndexOf( ")" ) - x.IndexOf( "(" ) - 1 ) )
   .Select( x => double.Parse(x))
.OrderBy( x => x ); // Or use OrderByDescending

编辑

I need the original strings, just ordered on those numbers.

基本上您需要做的是将谓词传递给 OrderBy 并告诉它按数字排序:

var items = strings.OrderBy(
   x => double.Parse( x.Substring( x.IndexOf( "(" ) + 1, 
   x.IndexOf( ")" ) - x.IndexOf( "(" ) - 1 ) ));

OO 方法如何?

我们正在订购字符串,但我们需要像对待数字一样对待它们。如果有一种方法我们可以调用 OrderBy 并由它为我们排序,那不是很好吗?嗯,有。 OrderBy 方法将使用 IComparable<T>(如果有的话)。让我们创建一个 class 来保存我们的 jpg 路径并实现 IComparable<T> 接口。

public class CustomJpg : IComparable<CustomJpg>
{
    public CustomJpg(string path)
    {
        this.Path = path;
    }

    public string Path { get; private set; }

    private double number = -1;

    // You can even make this public if you want.
    private double Number
    {
        get
        {
            // Let's cache the number for subsequent calls
            if (this.number == -1)
            {
                int myStart = this.Path.IndexOf("(") + 1;
                int myEnd = this.Path.IndexOf(")");
                string myNumber = this.Path.Substring(myStart, myEnd - myStart);
                double myVal;
                if (double.TryParse(myNumber, out myVal))
                {
                    this.number = myVal;
                }
                else
                {
                    throw new ArgumentException(string.Format("{0} has no parenthesis or a number between parenthesis.", this.Path));
                }
            }

            return this.number;
        }
    }

    public int CompareTo(CustomJpg other)
    {
        if (other == null)
        {
            return 1;
        }

        return this.Number.CompareTo(other.Number);
    }
}

上述方法的优点在于,如果我们继续调用 OrderBy,它就不必搜索开头 ( 和结尾 ) 并解析每次的编号。它在第一次调用时缓存它,然后继续使用它。另一件好事是我们可以绑定到 Path 属性 和 Number (我们必须将访问修饰符从私有更改)。我们甚至可以引入一个新的 属性 来保存缩略图并绑定到它。如您所见,这种方法更加灵活、简洁并且是一种 OO 方法。另外,查找数字的代码在一个地方,所以如果我们从 () 切换到另一个符号,我们只需在一个地方更改它。或者我们可以修改为先查找 (),如果找不到则查找另一个符号。

用法如下:

List<CustomJpg> jpgs = new List<CustomJpg>
{
    new CustomJpg("some-name-(1).jpg"),
    new CustomJpg("some-name-(5).jpg"),
    new CustomJpg("some-name-(5.1).jpg"),
    new CustomJpg("some-name-(6).jpg"),
    new CustomJpg("some-name-(12).jpg")
};

var ordered = jpgs.OrderBy(x => x).ToList();

您可以对任何对象使用这种方法。

如果您先切掉从右括号 ")" 开始的部分,您可以使子串选择更容易。也就是说,从 "some-name-(5.1).jpg" 你首先得到 "some-name-(5.1"。然后接"("之后的部分。这节省了长度计算,因为第二个 Substring 自动将所有内容都带到字符串的末尾。

strings = strings
    .OrderBy(x => Decimal.Parse(
                      x.Substring(0, x.IndexOf(")"))
                       .Substring(x.IndexOf("(") + 1)
                  )
     )
    .ToList();

这在这里可能不是很重要,但一般来说,decimaldouble 更准确地存储以十进制表示法给出的数字。 double 可以将 17.2 转换为 17.19999999999999.

在上面的示例代码中 return 一个按数字排序的数字列表,但是如果你想要更好地按名称排序的文件名列表,你可以在数字的开头输入相同的零,例如 "some-name-(001).jpg" 你可以简单地 命令

List<string> strings = new List<string>
        {
            "some-name-(001).jpg",
            "some-name-(005.1).jpg",
            "some-name-(005).jpg",
            "some-name-(004).jpg",
            "some-name-(006).jpg",
            "some-name-(012).jpg"
        };
        var orederedByName =strings.Select(s =>s ).OrderBy(s=>s);