C# 排序列表<;字符串>;在C中#

C# 排序列表<;字符串>;在C中#,c#,natural-sort,C#,Natural Sort,如何根据项目的整数值对列表进行排序 清单如下 "1" "5" "3" "6" "11" "9" "NUM1" "NUM0" 结果应该是这样的 "1" "3" "5" "6" "9" "11" "NUM0" "NUM1" 使用LINQ或Lambda表达式可以实现这一点吗 提前感谢我认为除了listName.Sort()之外,您不需要任何东西,因为Sort()方法使用默认的比较器来快速排序节点。默认比较器正是您感兴趣的功能。如何: list.Sort((x, y) => {

如何根据项目的整数值对列表进行排序

清单如下

"1"
"5"
"3"
"6"
"11"
"9"
"NUM1"
"NUM0"
结果应该是这样的

"1"
"3"
"5"
"6"
"9"
"11"
"NUM0"
"NUM1"
使用LINQ或Lambda表达式可以实现这一点吗


提前感谢

我认为除了listName.Sort()之外,您不需要任何东西,因为Sort()方法使用默认的比较器来快速排序节点。默认比较器正是您感兴趣的功能。

如何:

    list.Sort((x, y) =>
    {
        int ix, iy;
        return int.TryParse(x, out ix) && int.TryParse(y, out iy)
              ? ix.CompareTo(iy) : string.Compare(x, y);
    });
这称为“自然排序顺序”,通常用于对文件名等项目进行排序

下面是一个幼稚的实现(从某种意义上说,它可能有很多unicode问题),它似乎做到了这一点:

您可以将下面的代码复制到中来执行和测试它

基本上,比较算法将识别字符串中的数字,并通过用前导零填充最短的数字来处理这些数字,因此,例如,两个字符串
“Test123Abc”
“Test7X”
应该像比较
“Test123Abc”
“Test007X”
一样进行比较,这会产生你想要的

然而,当我说“幼稚”时,我的意思是我可能在这里有很多真正的unicode问题,比如处理变音符号和多码点字符。如果有人能提供更好的实现,我很乐意看到它

注:

  • 该实现实际上并不解析数字,因此任意长的数字应该可以正常工作
  • 由于它实际上没有将数字解析为“数字”,因此浮点数字将无法正确处理,“123.45”与“123.789”将被比较为“123.045”与“123.789”,这是错误的
代码:

void Main()
{
列表输入=新列表
{
“1”、“5”、“3”、“6”、“11”、“9”、“A1”、“A0”
};
var output=input.NaturalSort();
output.Dump();
}
公共静态类扩展
{
公共静态IEnumerable NaturalSort(
这是我的收藏)
{
返回NaturalSort(集合,CultureInfo.CurrentCulture);
}
公共静态IEnumerable NaturalSort(
此IEnumerable集合(CultureInfo CultureInfo)
{
return collection.OrderBy(s=>s,新的自然比较程序(cultureInfo));
}
私人类自然比较者:IComparer
{
私有只读文化信息(CultureInfo);;
公共自然比较(CultureInfo CultureInfo)
{
_CultureInfo=CultureInfo;
}
公共整数比较(字符串x、字符串y)
{
//简单案例
if(x==y)//也处理null
返回0;
如果(x==null)
返回-1;
如果(y==null)
返回+1;
int ix=0;
int-iy=0;
而(ix
杰夫·阿特伍德(Jeff Atwood)有一篇关于自然排序的文章,他在文章中链接到了所需算法的一些可用实现

Jeffs的其中一个链接指向如何拥有:

/*
*Alphanum算法是一种改进的字符串排序算法
*包含数字。而不是像这样按ASCII顺序对数字进行排序
*作为一种标准排序,该算法按数字顺序对数字进行排序。
*
*Alphanum算法在http://www.DaveKoelle.com
*
*基于Dave Koelle的Alphanum算法的Java实现。
*乔纳森·拉克伍德撰稿
*
*由Dominik Hurnaus改编为
*-正确排序一个单词以另一个单词开头的单词
*-性能稍好
*
*根据麻省理工学院许可证发布-https://opensource.org/licenses/MIT
*
*特此免费向获得许可的任何人授予许可
*本软件及相关文档文件(“软件”)的副本,
*不受限制地处理软件,包括但不限于
*使用、复制、修改、合并、发布、分发、再许可、,
*和/或出售软件副本,并允许
*为此提供的软件应符合以下条件:
*
*应包括上述版权声明和本许可声明
*在
void Main()
{
    List<string> input = new List<string>
    {
        "1", "5", "3", "6", "11", "9", "A1", "A0"
    };
    var output = input.NaturalSort();
    output.Dump();
}

public static class Extensions
{
    public static IEnumerable<string> NaturalSort(
        this IEnumerable<string> collection)
    {
        return NaturalSort(collection, CultureInfo.CurrentCulture);
    }

    public static IEnumerable<string> NaturalSort(
        this IEnumerable<string> collection, CultureInfo cultureInfo)
    {
        return collection.OrderBy(s => s, new NaturalComparer(cultureInfo));
    }

    private class NaturalComparer : IComparer<string>
    {
        private readonly CultureInfo _CultureInfo;

        public NaturalComparer(CultureInfo cultureInfo)
        {
            _CultureInfo = cultureInfo;
        }

        public int Compare(string x, string y)
        {
            // simple cases
            if (x == y) // also handles null
                return 0;
            if (x == null)
                return -1;
            if (y == null)
                return +1;

            int ix = 0;
            int iy = 0;
            while (ix < x.Length && iy < y.Length)
            {
                if (Char.IsDigit(x[ix]) && Char.IsDigit(y[iy]))
                {
                    // We found numbers, so grab both numbers
                    int ix1 = ix++;
                    int iy1 = iy++;
                    while (ix < x.Length && Char.IsDigit(x[ix]))
                        ix++;
                    while (iy < y.Length && Char.IsDigit(y[iy]))
                        iy++;
                    string numberFromX = x.Substring(ix1, ix - ix1);
                    string numberFromY = y.Substring(iy1, iy - iy1);

                    // Pad them with 0's to have the same length
                    int maxLength = Math.Max(
                        numberFromX.Length,
                        numberFromY.Length);
                    numberFromX = numberFromX.PadLeft(maxLength, '0');
                    numberFromY = numberFromY.PadLeft(maxLength, '0');

                    int comparison = _CultureInfo
                        .CompareInfo.Compare(numberFromX, numberFromY);
                    if (comparison != 0)
                        return comparison;
                }
                else
                {
                    int comparison = _CultureInfo
                        .CompareInfo.Compare(x, ix, 1, y, iy, 1);
                    if (comparison != 0)
                        return comparison;
                    ix++;
                    iy++;
                }
            }

            // we should not be here with no parts left, they're equal
            Debug.Assert(ix < x.Length || iy < y.Length);

            // we still got parts of x left, y comes first
            if (ix < x.Length)
                return +1;

            // we still got parts of y left, x comes first
            return -1;
        }
    }
}
/*
 * The Alphanum Algorithm is an improved sorting algorithm for strings
 * containing numbers.  Instead of sorting numbers in ASCII order like
 * a standard sort, this algorithm sorts numbers in numeric order.
 *
 * The Alphanum Algorithm is discussed at http://www.DaveKoelle.com
 *
 * Based on the Java implementation of Dave Koelle's Alphanum algorithm.
 * Contributed by Jonathan Ruckwood <jonathan.ruckwood@gmail.com>
 *
 * Adapted by Dominik Hurnaus <dominik.hurnaus@gmail.com> to
 *   - correctly sort words where one word starts with another word
 *   - have slightly better performance
 *
 * Released under the MIT License - https://opensource.org/licenses/MIT
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */
using System;
using System.Collections;
using System.Text;

/*
 * Please compare against the latest Java version at http://www.DaveKoelle.com
 * to see the most recent modifications
 */
namespace AlphanumComparator
{
    public class AlphanumComparator : IComparer
    {
        private enum ChunkType {Alphanumeric, Numeric};
        private bool InChunk(char ch, char otherCh)
        {
            ChunkType type = ChunkType.Alphanumeric;

            if (char.IsDigit(otherCh))
            {
                type = ChunkType.Numeric;
            }

            if ((type == ChunkType.Alphanumeric && char.IsDigit(ch))
                || (type == ChunkType.Numeric && !char.IsDigit(ch)))
            {
                return false;
            }

            return true;
        }

        public int Compare(object x, object y)
        {
            String s1 = x as string;
            String s2 = y as string;
            if (s1 == null || s2 == null)
            {
                return 0;
            }

            int thisMarker = 0, thisNumericChunk = 0;
            int thatMarker = 0, thatNumericChunk = 0;

            while ((thisMarker < s1.Length) || (thatMarker < s2.Length))
            {
                if (thisMarker >= s1.Length)
                {
                    return -1;
                }
                else if (thatMarker >= s2.Length)
                {
                    return 1;
                }
                char thisCh = s1[thisMarker];
                char thatCh = s2[thatMarker];

                StringBuilder thisChunk = new StringBuilder();
                StringBuilder thatChunk = new StringBuilder();

                while ((thisMarker < s1.Length) && (thisChunk.Length==0 ||InChunk(thisCh, thisChunk[0])))
                {
                    thisChunk.Append(thisCh);
                    thisMarker++;

                    if (thisMarker < s1.Length)
                    {
                        thisCh = s1[thisMarker];
                    }
                }

                while ((thatMarker < s2.Length) && (thatChunk.Length==0 ||InChunk(thatCh, thatChunk[0])))
                {
                    thatChunk.Append(thatCh);
                    thatMarker++;

                    if (thatMarker < s2.Length)
                    {
                        thatCh = s2[thatMarker];
                    }
                }

                int result = 0;
                // If both chunks contain numeric characters, sort them numerically
                if (char.IsDigit(thisChunk[0]) && char.IsDigit(thatChunk[0]))
                {
                    thisNumericChunk = Convert.ToInt32(thisChunk.ToString());
                    thatNumericChunk = Convert.ToInt32(thatChunk.ToString());

                    if (thisNumericChunk < thatNumericChunk)
                    {
                        result = -1;
                    }

                    if (thisNumericChunk > thatNumericChunk)
                    {
                        result = 1;
                    }
                }
                else
                {
                    result = thisChunk.ToString().CompareTo(thatChunk.ToString());
                }

                if (result != 0)
                {
                    return result;
                }
            }

            return 0;
        }
    }
}
public class NameAndNumber
{
    public NameAndNumber(string s)
    {
        OriginalString = s;
        Match match = Regex.Match(s,@"^(.*?)(\d*)$");
        Name = match.Groups[1].Value;
        int number;
        int.TryParse(match.Groups[2].Value, out number);
        Number = number; //will get default value when blank
    }

    public string OriginalString { get; private set; }
    public string Name { get; private set; }
    public int Number { get; private set; }
}
var list = new List<string> { "ABC", "1", "5", "NUM44", "3", 
                              "6", "11", "9", "NUM1", "NUM0" };

var sorted = list.Select(str => new NameAndNumber(str))
    .OrderBy(n => n.Name)
    .ThenBy(n => n.Number);
static void Sort()
{
    string[] partNumbers = new string[] {"A1", "A2", "A10", "A111"};
    string[] result = partNumbers.OrderBy(x => PadNumbers(x)).ToArray();
}


public static string PadNumbers(string input)
{
        const int MAX_NUMBER_LEN = 10;

        string newInput = "";
        string currentNumber = "";
        foreach (char a in input)
        {
            if (!char.IsNumber(a))
            {
                if (currentNumber == "")
                {
                    newInput += a;
                    continue;
                }
                newInput += "0000000000000".Substring(0, MAX_NUMBER_LEN - currentNumber.Length) + currentNumber;
                currentNumber = "";
            }
            currentNumber += a;
        }
        if (currentNumber != "")
        {
            newInput += "0000000000000".Substring(0, MAX_NUMBER_LEN - currentNumber.Length) + currentNumber;
        }

        return newInput;
    }
    var numericList = a.Where(i => int.TryParse(i, out _)).OrderBy(j => int.Parse(j)).ToList();
    var nonNumericList = a.Where(i => !int.TryParse(i, out _)).OrderBy(j => j).ToList();
    a.Clear();
    a.AddRange(numericList);
    a.AddRange(nonNumericList);