我试图以独特的方式订购混合字符串,并且想知道其他人是否已经这样做了。我找到了几篇关于使用IOrderConparer的文章,但找不到我特定排序问题的解决方案。
我有以下内容:
1017,650,650C,650B,W323,10,20,1000,W1000
我需要按如下方式订购:
10,20,650,650B,650C,1000,1017,W323,W1000
任何帮助将不胜感激。感谢。
答案 0 :(得分:1)
使用StrCmpLogicalW实现比较:
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
public static extern int StrCmpLogicalW(string x, string y);
这将是一个逻辑(将数字考虑在内)字符串比较而不是基于标准字符串的字符串,它将为您提供您所追求的结果。
答案 1 :(得分:1)
稍长但所有托管代码
public class LogicalSorter : IComparer
{
public int Compare(object a, object b)
{
var first = Regex.Split((string)a,"([0-9]+)").Where(s => s != "").ToArray();
var second = Regex.Split((string)b,"([0-9]+)").Where(s => s != "").ToArray();
var endIdx = Math.Min(first.Count(), second.Count());
for (var i = 0; i < endIdx; i++)
{
var part1 = first.ElementAt(i);
var part2 = second.ElementAt(i);
if (part1.All(char.IsDigit) && part2.All(char.IsDigit) && part1 != part2)
{
return int.Parse(part1).CompareTo(int.Parse(part2));
}
if (part1 != part2) return part1.CompareTo(part2);
}
return first.Count().CompareTo(second.Count());
}
}
像这样使用
string[] values = { "1017", "650", "650C", "650B", "W323", "10", "20", "1000", "W1000" };
Array.Sort(values, new LogicalSorter());
foreach (var value in values)
Console.WriteLine(value);
或使用Mrinal建议的泛型(首选)
public class LogicalSorter : IComparer<String>
{
public int Compare(String a, String b)
{
var first = Regex.Split(a, "([0-9]+)").Where(s => s != "").ToArray();
var second = Regex.Split(b, "([0-9]+)").Where(s => s != "").ToArray();
var endIdx = Math.Min(first.Count(), second.Count());
for (var i = 0; i < endIdx; i++)
{
var part1 = first.ElementAt(i);
var part2 = second.ElementAt(i);
if (part1.All(char.IsDigit) && part2.All(char.IsDigit) && part1 != part2)
{
return int.Parse(part1).CompareTo(int.Parse(part2));
}
if (part1 != part2) return part1.CompareTo(part2);
}
return first.Count().CompareTo(second.Count());
}
}
优化托管代码示例(速度不适用于外观),执行 47x 正则表达式版本
public class LogicalSorter : IComparer<String>
{
public int Compare(String a, String b)
{
var aLength = a.Length;
var bLength = b.Length;
var aIdx = 0;
var bIdx = 0;
int aPartLen;
int bPartLen;
int aPartEndIndex;
int bPartEndIndex;
bool aIsString;
bool bIsString;
// Examine both strings on character level, keep track of where
// we are in each string since lengths might differ
while (aIdx < aLength && bIdx < bLength)
{
// If both strings contain digit at current index
// compare numbers
if (char.IsDigit(a[aIdx]) && char.IsDigit(b[bIdx]))
{
// Get longest consecutive list of digits from each string
aPartEndIndex = aIdx;
while (aPartEndIndex < aLength && char.IsDigit(a[aPartEndIndex])) { aPartEndIndex++; }
bPartEndIndex = bIdx;
while (bPartEndIndex < bLength && char.IsDigit(b[bPartEndIndex])) { bPartEndIndex++; }
aPartLen = aPartEndIndex - aIdx;
bPartLen = bPartEndIndex - bIdx;
// Compare lengths (longest number is greater)
if (aPartLen != bPartLen) return aPartLen < bPartLen ? -1 : 1;
// Same length numbers, compare chars until not same or end
while (aIdx < aPartEndIndex && a[aIdx] == b[bIdx])
{
aIdx++;
bIdx++;
}
// If not at end compare last characters that were not same
if(aIdx != aPartEndIndex)
return a[aIdx] < b[bIdx] ? -1 : 1;
}
else
{
// Comparing string vs number or string vs string
aIsString = char.IsLetter(a[aIdx]);
bIsString = char.IsLetter(b[bIdx]);
// if not 2 strings, number is always first
if (aIsString != bIsString) return aIsString ? 1 : -1;
// Get longest consecutive list of letters from each string
aPartEndIndex = aIdx;
while (aPartEndIndex < aLength && (char.IsLetter(a[aPartEndIndex]) == aIsString))
{
aPartEndIndex++;
}
bPartEndIndex = bIdx;
while (bPartEndIndex < bLength && (char.IsLetter(b[bPartEndIndex]) == bIsString))
{
bPartEndIndex++;
}
// Compare chars until not same or end
while (aIdx < aPartEndIndex && bIdx < bPartEndIndex && a[aIdx] == b[bIdx])
{
aIdx++;
bIdx++;
}
// if not at end compare last letters found
if ((aIdx != aPartEndIndex) || (bIdx != bPartEndIndex))
return a[aIdx] < b[bIdx] ? -1 : 1;
}
}
// Use length as tie breaker
return aLength < bLength ? -1 : 1;
}
}