如何为含Animal元素的字符串列表自定义Sort()排序并修正数字排序问题
C#字符串列表按Animal分组+自然排序方案
核心需求实现
通过自定义IComparer<string>实现Sort()方法的定制排序,同时解决数字排序异常问题:
- 保留非
<Animal .../>及<Animal>标签的元素,按字符串默认顺序排在列表前端(可调整) <Animal>容器标签置于所有Animal数据标签之前- Animal数据标签先按
Group属性排序,同组内按Name做自然排序(解决Horse1、Horse10、Horse2的顺序问题)
自定义比较器代码
public class AnimalStringComparer : IComparer<string> { public int Compare(string x, string y) { // 标记元素类型 bool isXAnimalData = x.TrimStart().StartsWith("<Animal Name=\""); bool isYAnimalData = y.TrimStart().StartsWith("<Animal Name=\""); bool isXAnimalContainer = x.Trim() == "<Animal>"; bool isYAnimalContainer = y.Trim() == "<Animal>"; // 普通元素之间的排序 if (!isXAnimalData && !isXAnimalContainer && !isYAnimalData && !isYAnimalContainer) { return string.Compare(x, y, StringComparison.Ordinal); } // 普通元素优先排在Animal相关元素前 if (!isXAnimalData && !isXAnimalContainer && (isYAnimalData || isYAnimalContainer)) return -1; if ((isXAnimalData || isXAnimalContainer) && !isYAnimalData && !isYAnimalContainer) return 1; // Animal容器标签之间相等 if (isXAnimalContainer && isYAnimalContainer) return 0; // 容器标签排在数据标签前 if (isXAnimalContainer && isYAnimalData) return -1; if (isYAnimalContainer && isXAnimalData) return 1; // Animal数据标签按Group、Name自然排序 var xGroup = ExtractAttribute(x, "Group"); var yGroup = ExtractAttribute(y, "Group"); int groupCompare = string.Compare(xGroup, yGroup, StringComparison.OrdinalIgnoreCase); if (groupCompare != 0) return groupCompare; var xName = ExtractAttribute(x, "Name"); var yName = ExtractAttribute(y, "Name"); return NaturalCompare(xName, yName); } // 提取XML属性值(简单实现,复杂场景建议用XElement解析) private string ExtractAttribute(string xml, string attrName) { int start = xml.IndexOf($"{attrName}=\""); if (start == -1) return string.Empty; start += $"{attrName}=\"".Length; int end = xml.IndexOf("\"", start); return end == -1 ? string.Empty : xml.Substring(start, end - start); } // 自然排序逻辑:区分字母与数字,按数值比较数字部分 private int NaturalCompare(string a, string b) { if (string.IsNullOrEmpty(a)) return string.IsNullOrEmpty(b) ? 0 : -1; if (string.IsNullOrEmpty(b)) return 1; int aIdx = 0, bIdx = 0; while (aIdx < a.Length && bIdx < b.Length) { if (char.IsDigit(a[aIdx]) && char.IsDigit(b[bIdx])) { string aNum = GetNumberPart(a, ref aIdx); string bNum = GetNumberPart(b, ref bIdx); if (long.TryParse(aNum, out long aLong) && long.TryParse(bNum, out long bLong)) { long numDiff = aLong - bLong; if (numDiff != 0) return numDiff > 0 ? 1 : -1; } else { int strDiff = string.Compare(aNum, bNum, StringComparison.Ordinal); if (strDiff != 0) return strDiff; } } else { int charDiff = char.ToLowerInvariant(a[aIdx]).CompareTo(char.ToLowerInvariant(b[bIdx])); if (charDiff != 0) return charDiff; aIdx++; bIdx++; } } return a.Length - b.Length; } // 提取连续数字段 private string GetNumberPart(string s, ref int idx) { int start = idx; while (idx < s.Length && char.IsDigit(s[idx])) idx++; return s.Substring(start, idx - start); } }
使用方式
直接将自定义比较器传入Sort()方法即可:
var somedata = new List<string> { "<some data>", " <some data else>", "</some data>", "<Animal>", " <Animal Name=\"Horse10\" Group=\"Equine\" Colour=\"Black\" />", " <Animal Name=\"Horse2\" Group=\"Equine\" Colour=\"Brown\" />", " <Animal Name=\"Lion\" Group=\"Feline\" Colour=\"Brown\" />", " <Animal Name=\"Horse1\" Group=\"Equine\" Colour=\"White\" />", "</some other data>" }; somedata.Sort(new AnimalStringComparer()); // 验证排序结果 foreach (var item in somedata) { Console.WriteLine(item); }
可调整项
- 若需将非Animal元素排在列表末尾,只需交换普通元素与Animal元素比较时的返回值(把
-1和1互换) - 如需区分
Group的大小写,将StringComparison.OrdinalIgnoreCase改为StringComparison.Ordinal - 复杂XML场景下,建议替换
ExtractAttribute方法为XElement.Parse(xml).Attribute(attrName)?.Value,避免字符串解析的局限性
内容的提问来源于stack exchange,提问作者orecchione bruno
相关产品推荐
相关产品推荐

