#c#
Вопрос:
Мне нужно сравнить номера разделов в документе, сначала я просто собирался преобразовать их в десятичные и проверить, больше ли одно число другого. Проблема в том, что в некоторых разделах есть несколько десятичных знаков.
Пример: Мне нужно выполнить математическое сравнение 1.1 с 1.1.2.3, чтобы определить, какой из них находится дальше в документе.
Для начала это строки, и мне, по сути, нужно провести некоторые математические сравнения с ними. Я думал об удалении десятичных знаков, а затем преобразовании в int, но это отбрасывает некоторые разделы, например, раздел 2 будет считаться меньшим, чем раздел 1.1, поскольку 1.1 будет изменен на 11, что нехорошо.
string section1 = "1.1";
string section2 = "2";
int convertedSection1 = Convert.ToInt32(section1.Replace(".",""));
int convertedSection2 = Convert.ToInt32(section2.Replace(".",""));
if(convertedSection1 < convertedSection2)
//This will incorrectly say 1.1 is greater than 2
string section1 = "1.1.2.4";
string section2 = "2.4";
decimal convertedSection1 = Convert.ToDecimal(section1);
decimal convertedSection2 = Convert.ToDecimal(section2);
if(convertedSection1 < convertedSection2)
//This will convert 1.1.2.4 to 1.1 which is no good
Комментарии:
1. 1.1.2.4 должно быть преобразовано в 1.124?
2. //Это приведет к преобразованию 1.1.2.4 в 1.1, что не очень хорошо — чего вы здесь ожидаете?
Ответ №1:
Вы можете создать класс, аналогичный Version
классу из .СЕТЕВАЯ структура. Если вы реализуете некоторые операторы и IComparable
, это действительно приятно.
Как это работает? Он преобразует заданную строку в список целых чисел. При сравнении он будет начинаться в начале каждого списка и сравнивать каждую отдельную часть.
public class Section: IComparable<Section>
{
// Stores all individual components of the section
private List<int> parts = new List<int>();
// Construct a section from a string
public Section(string section)
{
var strings = section.Split('.');
foreach (var s in strings)
{
parts.Add(int.Parse(s));
}
}
// Make it nice for display
public override string ToString()
{
return string.Join(".", parts);
}
// Implement comparison operators for convenience
public static bool operator ==(Section a, Section b)
{
// Comparing the identical object
if (ReferenceEquals(a, b)) return true;
// One object is null and the other isn't
if ((object)a == null) return false;
if ((object)b == null) return false;
// Different amount of items
if (a.parts.Count != b.parts.Count) return false;
// Check all individual items
for (int i=0; i<a.parts.Count;i )
{
if (a.parts[i] != b.parts[i]) return false;
}
return true;
}
public static bool operator !=(Section a, Section b)
{
return !(a == b);
}
public static bool operator <(Section a, Section b)
{
// Use minimum, otherwise we exceed the index
for (int i=0; i< Math.Min(a.parts.Count, b.parts.Count); i )
{
if (a.parts[i] < b.parts[i]) return true;
}
if (b.parts.Count > a.parts.Count) return true;
return false;
}
public static bool operator >(Section a, Section b)
{
// Use minimum, otherwise we exceed the index
for (int i = 0; i < Math.Min(a.parts.Count, b.parts.Count); i )
{
if (a.parts[i] > b.parts[i]) return true;
}
if (a.parts.Count > b.parts.Count) return true;
return false;
}
// Implement the IComparable interface for sorting
public int CompareTo(Section other)
{
if (this == other) return 0;
if (this < other) return -1;
return 1;
}
}
Тесты на покрытие 96% :
Assert.IsTrue(new Section("1.2.3.4") > new Section("1.2.3"));
Assert.IsFalse(new Section("1.2.3.4") < new Section("1.2.3"));
Assert.IsFalse(new Section("1.2.3.4") == new Section("1.2.3"));
Assert.IsTrue(new Section("1.2.3.4") == new Section("1.2.3.4"));
Assert.IsFalse(new Section("1.2.3.4") == new Section("1.2.3.5"));
Assert.IsTrue(new Section("1.2.3.4") != new Section("1.2.3.5"));
var sec = new Section("1");
Assert.IsTrue(sec == sec);
Assert.AreEqual("1.2.3.4", new Section("1.2.3.4").ToString());
var sortTest = new List<Section> { new Section("2"), new Section("1.2"), new Section("1"), new Section("3.1") };
sortTest.Sort();
var expected = new List<Section> { new Section("1"), new Section("1.2"), new Section("2"), new Section("3.1") };
CollectionAssert.AreEqual(expected, sortTest, new SectionComparer());
Ответ №2:
Если вы знаете, что строки ваших разделов всегда хорошо сформированы, и вы знаете, что они не заходят глубже, чем на 6 уровней, и что ни один уровень не содержит более 999 элементов, то это хорошо работает:
string zero = ".0.0.0.0.0.0";
long Section2Long(string section) =>
(section zero)
.Split('.')
.Take(6)
.Select(t => long.Parse(t))
.Aggregate((x, y) => x * 1000 y);
Теперь, если у меня есть это:
string[] sections = new []
{
"1.2.4", "2.3", "1", "1.2", "1.1.1.1", "1.0.0.1.0.1", "2.2.9"
};
Я легко могу разобраться в этом так:
string[] sorted = sections.OrderBy(x => Section2Long(x)).ToArray();
Я получаю этот вывод:
1
1.0.0.1.0.1
1.1.1.1
1.2
1.2.4
2.2.9
2.3