Эффективный способ сравнения строк версии в Java

Возможный дубликат:
Как вы сравниваете две строки версии в Java?

У меня есть 2 строки, которые содержат информацию о версии, как показано ниже:

str1 = "1.2" str2 = "1.1.2" 

Теперь, может ли кто-нибудь сказать мне эффективный способ сравнить эти версии внутри строк в Java & return 0, если они равны, -1, если str1 str2.

 /** * Compares two version strings. * * Use this instead of String.compareTo() for a non-lexicographical * comparison that works for version strings. eg "1.10".compareTo("1.6"). * * @note It does not work if "1.10" is supposed to be equal to "1.10.0". * * @param str1 a string of ordinal numbers separated by decimal points. * @param str2 a string of ordinal numbers separated by decimal points. * @return The result is a negative integer if str1 is _numerically_ less than str2. * The result is a positive integer if str1 is _numerically_ greater than str2. * The result is zero if the strings are _numerically_ equal. */ public static int versionCompare(String str1, String str2) { String[] vals1 = str1.split("\\."); String[] vals2 = str2.split("\\."); int i = 0; // set index to first non-equal ordinal or length of shortest version string while (i < vals1.length && i < vals2.length && vals1[i].equals(vals2[i])) { i++; } // compare first non-equal ordinal number if (i < vals1.length && i < vals2.length) { int diff = Integer.valueOf(vals1[i]).compareTo(Integer.valueOf(vals2[i])); return Integer.signum(diff); } // the strings are equal or one string is a substring of the other // eg "1.2.3" = "1.2.3" or "1.2.3" < "1.2.3.4" return Integer.signum(vals1.length - vals2.length); } 

Как указывали другие, String.split () – это очень простой способ выполнить желаемое сравнение, и Майк Пак делает превосходный момент, что с такими (вероятными) короткими строками он, вероятно, не имеет большого значения, но что Привет! Если вы хотите провести сравнение без ручного анализа строки, и у вас есть возможность отказаться от раннего, вы можете попробовать class java.util.Scanner .

 public static int versionCompare(String str1, String str2) { try ( Scanner s1 = new Scanner(str1); Scanner s2 = new Scanner(str2);) { s1.useDelimiter("\\."); s2.useDelimiter("\\."); while (s1.hasNextInt() && s2.hasNextInt()) { int v1 = s1.nextInt(); int v2 = s2.nextInt(); if (v1 < v2) { return -1; } else if (v1 > v2) { return 1; } } if (s1.hasNextInt() && s1.nextInt() != 0) return 1; //str1 has an additional lower-level version number if (s2.hasNextInt() && s2.nextInt() != 0) return -1; //str2 has an additional lower-level version return 0; } // end of try-with-resources } 

Я хотел сделать это сам, и я вижу три разных подхода к этому, и до сих пор почти все разделяют строки версии. Я не считаю, что это эффективно, хотя размер кода мудрый он хорошо читает и выглядит хорошо.

подходы:

  1. Предположим, что верхний предел числа секций (ординалов) в строке версии, а также предел представленного там значения. Часто 4 точки макс и максимум 999 для любого ординала. Вы можете видеть, куда это происходит, и он преобразует версию в соответствии с строкой типа «1.0» => «001000000000» со строковым форматом или каким-либо другим способом для заполнения каждого ординала. Затем сравните строку.
  2. Разделите строки на порядковый разделитель (‘.’) И проведите по ним и сравните анализируемую версию. Этот подход хорошо продемонстрировал Алекс Гительман.
  3. Сравнение ординалов при анализе их из строк версии. Если бы все строки были просто указателями на массивы символов, как в C, тогда это был бы четкий подход (где вы заменили бы «.» Нулевым терминатором, поскольку он был найден, и перемещали около 2 или 4 указателя.

Мысли о трех подходах:

  1. Был связанный блог, в котором показано, как перейти с 1. Ограничения указаны в строке строки строки, количестве разделов и максимальном значении раздела. Я не думаю, что это безумие иметь такую ​​строку, которая ломает 10 000 в какой-то момент. Кроме того, большинство реализаций по-прежнему разбивают строку.
  2. Разделение строк заранее ясно, чтобы читать и думать, но мы каждую секунду проследуем каждую строку, чтобы сделать это. Я хотел бы сравнить, как это происходит со следующим подходом.
  3. Сравнение строки по мере ее разделения дает вам преимущество в том, что вы можете прекратить расщепление в начале сравнения: «2.1001.100101.9999998» до «1.0.0.0.0.0.1.0.0.0.1». Если бы это были C, а не Java, преимущества могли бы ограничивать объем памяти, выделенный для новых строк для каждого раздела каждой версии, но это не так.

Я не видел, чтобы кто-то показывал пример этого третьего подхода, поэтому я хотел бы добавить его здесь, чтобы получить ответ.

 public class VersionHelper { /** * Compares one version string to another version string by dotted ordinals. * eg. "1.0" > "0.09" ; "0.9.5" < "0.10", * also "1.0" < "1.0.0" but "1.0" == "01.00" * * @param left the left hand version string * @param right the right hand version string * @return 0 if equal, -1 if thisVersion < comparedVersion and 1 otherwise. */ public static int compare(@NotNull String left, @NotNull String right) { if (left.equals(right)) { return 0; } int leftStart = 0, rightStart = 0, result; do { int leftEnd = left.indexOf('.', leftStart); int rightEnd = right.indexOf('.', rightStart); Integer leftValue = Integer.parseInt(leftEnd < 0 ? left.substring(leftStart) : left.substring(leftStart, leftEnd)); Integer rightValue = Integer.parseInt(rightEnd < 0 ? right.substring(rightStart) : right.substring(rightStart, rightEnd)); result = leftValue.compareTo(rightValue); leftStart = leftEnd + 1; rightStart = rightEnd + 1; } while (result == 0 && leftStart > 0 && rightStart > 0); if (result == 0) { if (leftStart > rightStart) { return containsNonZeroValue(left, leftStart) ? 1 : 0; } if (leftStart < rightStart) { return containsNonZeroValue(right, rightStart) ? -1 : 0; } } return result; } private static boolean containsNonZeroValue(String str, int beginIndex) { for (int i = beginIndex; i < str.length(); i++) { char c = str.charAt(i); if (c != '0' && c != '.') { return true; } } return false; } } 

Единичный тест, демонстрирующий ожидаемый результат.

 public class VersionHelperTest { @Test public void testCompare() throws Exception { assertEquals(1, VersionHelper.compare("1", "0.9")); assertEquals(1, VersionHelper.compare("0.0.0.2", "0.0.0.1")); assertEquals(1, VersionHelper.compare("1.0", "0.9")); assertEquals(1, VersionHelper.compare("2.0.1", "2.0.0")); assertEquals(1, VersionHelper.compare("2.0.1", "2.0")); assertEquals(1, VersionHelper.compare("2.0.1", "2")); assertEquals(1, VersionHelper.compare("0.9.1", "0.9.0")); assertEquals(1, VersionHelper.compare("0.9.2", "0.9.1")); assertEquals(1, VersionHelper.compare("0.9.11", "0.9.2")); assertEquals(1, VersionHelper.compare("0.9.12", "0.9.11")); assertEquals(1, VersionHelper.compare("0.10", "0.9")); assertEquals(0, VersionHelper.compare("0.10", "0.10")); assertEquals(-1, VersionHelper.compare("2.10", "2.10.1")); assertEquals(-1, VersionHelper.compare("0.0.0.2", "0.1")); assertEquals(1, VersionHelper.compare("1.0", "0.9.2")); assertEquals(1, VersionHelper.compare("1.10", "1.6")); assertEquals(0, VersionHelper.compare("1.10", "1.10.0.0.0.0")); assertEquals(1, VersionHelper.compare("1.10.0.0.0.1", "1.10")); assertEquals(0, VersionHelper.compare("1.10.0.0.0.0", "1.10")); assertEquals(1, VersionHelper.compare("1.10.0.0.0.1", "1.10")); } } 

Это почти наверняка не самый эффективный способ сделать это, но учитывая, что строки с номерами версий почти всегда будут всего лишь несколькими символами, я не думаю, что это стоит оптимизировать дальше:

 public static int compareVersions(String v1, String v2) { String[] components1 = v1.split("\\."); String[] components2 = v2.split("\\."); int length = Math.min(components1.length, components2.length); for(int i = 0; i < length; i++) { int result = new Integer(components1[i]).compareTo(Integer.parseInt(components2[i])); if(result != 0) { return result; } } return Integer.compare(components1.length, components2.length); } 

Разделите строку на “.” или независимо от того, какой будет ваш делиметр, затем проанализируйте каждый из этих токенов до значения Integer и сравните.

 int compareStringIntegerValue(String s1, String s2, String delimeter) { String[] s1Tokens = s1.split(delimeter); String[] s2Tokens = s2.split(delimeter); int returnValue = 0; if(s1Tokens.length > s2Tokens.length) { for(int i = 0; i 

Я оставлю другое заявление if в качестве упражнения.

Сравнение строк версий может быть беспорядочным; вы получаете бесполезные ответы, потому что единственный способ сделать эту работу – быть очень конкретным в отношении вашего соглашения о заказе. Я видел одну относительно короткую и полную функцию сравнения версий в сообщении в блоге , причем код размещен в общедоступном домене – это не на Java, но должно быть просто увидеть, как его адаптировать.

Адаптировано из ответа Алекса Гительмана.

 int compareVersions( String str1, String str2 ){ if( str1.equals(str2) ) return 0; // Short circuit when you shoot for efficiency String[] vals1 = str1.split("\\."); String[] vals2 = str2.split("\\."); int i=0; // Most efficient way to skip past equal version subparts while( i 

Итак, как вы можете видеть, не так уж и тривиально. Также это терпит неудачу, когда вы разрешаете вести 0, но я никогда не видел версию «1.04.5» или w / e. Вам нужно будет использовать целочисленное сравнение в цикле while, чтобы исправить это. Это становится еще более сложным, когда вы смешиваете буквы с номерами в строках версии.

Разделите их на массивы, а затем сравните.

 // check if two strings are equal. If they are return 0; String[] a1; String[] a2; int i = 0; while (true) { if (i == a1.length && i < a2.length) return -1; else if (i < a1.length && i == a2.length) return 1; if (a1[i].equals(a2[i]) { i++; continue; } return a1[i].compareTo(a2[i]; } return 0; 

Я бы разделил проблему на две части, составив и сравнив. Если вы можете предположить, что формат правильный, то сравнение версий только чисел очень просто:

 final int versionA = Integer.parseInt( "01.02.00".replaceAll( "\\.", "" ) ); final int versionB = Integer.parseInt( "01.12.00".replaceAll( "\\.", "" ) ); 

Тогда обе версии можно сравнить как целые. Таким образом, «большая проблема» – это формат, но это может иметь много правил. В моем случае я просто заполняю минимум две пары цифр, поэтому формат «99.99.99» всегда, а затем я делаю вышеуказанное преобразование; поэтому в моем случае логика программы находится в форматировании, а не в сравнении версий. Теперь, если вы делаете что-то очень конкретное и, возможно, можете доверять началу строки версии, возможно, вы просто можете проверить длину строки версии, а затем просто сделать преобразование int … но я думаю, что это лучшая практика для убедитесь, что формат соответствует ожиданиям.

Шаг 1: Используйте StringTokenizer в java с точкой в ​​качестве разделителя

StringTokenizer(String str, String delimiters) или

Вы можете использовать String.split() и Pattern.split() , разделить на точку и затем преобразовать каждую строку в Integer с помощью Integer.parseInt(String str)

Шаг 2. Сравните целое число слева направо.

  • Как получить часть файла после строки, которая соответствует выражению grep? (первое совпадение)
  • Regex, как совместить необязательный символ
  • Как преобразовать String в ArrayList?
  • Bash: манипуляция со строками (знак процента)
  • Как использовать «.» Как разделитель с String.split () в java
  • Что означает {0}, когда он найден в строке в C #?
  • C ++ printf с std :: string?
  • Как форматировать строки в Java
  • Строковые литералы: куда они идут?
  • Java: String split (): Я хочу, чтобы он включал пустые строки в конце
  • Как разобрать строку с десятичной точкой в ​​double?
  • Interesting Posts

    Добавление элемента div в тело или документ в JavaScript

    Как создать ссылки в текстовом клиенте?

    Использует ли перехват целых чисел без знака?

    Внедрение PCI-Passthrough с Linux-KVM на Debian

    Можем ли мы вернуть объекты, имеющие удаленный / закрытый экземпляр / механизм перемещения по значению из функции?

    Spring @PropertySource с использованием YAML

    Пакет конфликтует с автоматическими модулями в Java 9

    Обновить переменную родительской области в угловом

    Используйте обычные клавиши в качестве клавиш-модификаторов, например, нажмите S и F вместе вместо Ctrl + S, чтобы сохранить файл

    Передача строкового литерала в качестве параметра в class шаблонов C ++

    Проверка jQuery на разных языках

    Тройная выходная видеокарта

    Использовать состояние или refs в компонентах формы React.js?

    Отправка электронной почты в Node.js?

    Группировать по нескольким столбцам в dplyr, используя ввод векторной строки

    Давайте будем гением компьютера.