Duplicata possible:
Comment comparez-vous deux chaînes de version en Java?
J'ai 2 chaînes qui contiennent des informations de version comme indiqué ci-dessous:
str1 = "1.2"
str2 = "1.1.2"
Maintenant, quelqu'un peut-il me dire le moyen efficace de comparer ces versions à l'intérieur de chaînes dans Java & return 0, si elles sont égales, -1, si str1 <str2 & 1 si str1> str2 .
Nécessite commons-lang3-3.8.1.jar pour les opérations de chaîne.
/**
* Compares two version strings.
*
* Use this instead of String.compareTo() for a non-lexicographical
* comparison that works for version strings. e.g. "1.10".compareTo("1.6").
*
* @param v1 a string of alpha numerals separated by decimal points.
* @param v2 a string of alpha numerals separated by decimal points.
* @return The result is 1 if v1 is greater than v2.
* The result is 2 if v2 is greater than v1.
* The result is -1 if the version format is unrecognized.
* The result is zero if the strings are equal.
*/
public int VersionCompare(String v1,String v2)
{
int v1Len=StringUtils.countMatches(v1,".");
int v2Len=StringUtils.countMatches(v2,".");
if(v1Len!=v2Len)
{
int count=Math.abs(v1Len-v2Len);
if(v1Len>v2Len)
for(int i=1;i<=count;i++)
v2+=".0";
else
for(int i=1;i<=count;i++)
v1+=".0";
}
if(v1.equals(v2))
return 0;
String[] v1Str=StringUtils.split(v1, ".");
String[] v2Str=StringUtils.split(v2, ".");
for(int i=0;i<v1Str.length;i++)
{
String str1="",str2="";
for (char c : v1Str[i].toCharArray()) {
if(Character.isLetter(c))
{
int u=c-'a'+1;
if(u<10)
str1+=String.valueOf("0"+u);
else
str1+=String.valueOf(u);
}
else
str1+=String.valueOf(c);
}
for (char c : v2Str[i].toCharArray()) {
if(Character.isLetter(c))
{
int u=c-'a'+1;
if(u<10)
str2+=String.valueOf("0"+u);
else
str2+=String.valueOf(u);
}
else
str2+=String.valueOf(c);
}
v1Str[i]="1"+str1;
v2Str[i]="1"+str2;
int num1=Integer.parseInt(v1Str[i]);
int num2=Integer.parseInt(v2Str[i]);
if(num1!=num2)
{
if(num1>num2)
return 1;
else
return 2;
}
}
return -1;
}
Comme d'autres l'ont souligné, String.split () est un moyen très facile de faire la comparaison que vous voulez, et Mike Deck fait l'excellent point qu'avec de telles cordes courtes (probables), cela n'aura probablement pas beaucoup d'importance, mais ce que le Hey! Si vous souhaitez effectuer la comparaison sans analyser manuellement la chaîne et avoir la possibilité de quitter plus tôt, vous pouvez essayer la classe 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
}
Je cherchais à le faire moi-même et je vois trois approches différentes pour le faire, et jusqu'à présent, presque tout le monde fractionne les chaînes de version. Je ne pense pas que cela soit efficace, bien que la taille du code soit bonne et semble bonne.
Approches:
Réflexions sur les trois approches:
Je n'ai vu personne donner un exemple de cette troisième approche, alors je voudrais l'ajouter ici comme une réponse à l'efficience.
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;
}
}
Test unitaire démontrant la sortie attendue.
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"));
}
}
C'est presque certainement pas le le plus moyen efficace de le faire, mais étant donné que les chaînes de numéro de version ne comporteront presque toujours que quelques caractères, je ne pense pas que cela vaille la peine d'être optimisé plus loin:
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);
}
Divisez-les en tableaux, puis comparez.
// 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;
Adapté de la réponse d'Alex Gitelman.
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<vals1.length && i<val2.length && vals[i].equals(vals[i]) ) i++;
// If we didn't reach the end,
if( i<vals1.length && i<val2.length )
// have to use integer comparison to avoid the "10"<"1" problem
return Integer.valueOf(vals1[i]).compareTo( Integer.valueOf(vals2[i]) );
if( i<vals1.length ){ // end of str2, check if str1 is all 0's
boolean allZeros = true;
for( int j = i; allZeros & (j < vals1.length); j++ )
allZeros &= ( Integer.parseInt( vals1[j] ) == 0 );
return allZeros ? 0 : -1;
}
if( i<vals2.length ){ // end of str1, check if str2 is all 0's
boolean allZeros = true;
for( int j = i; allZeros & (j < vals2.length); j++ )
allZeros &= ( Integer.parseInt( vals2[j] ) == 0 );
return allZeros ? 0 : 1;
}
return 0; // Should never happen (identical strings.)
}
Donc, comme vous pouvez le voir, pas si trivial. Cela échoue également lorsque vous autorisez les 0 à gauche, mais je n'ai jamais vu de version "1.04.5" ou w/e. Vous devrez utiliser la comparaison d'entiers dans la boucle while pour résoudre ce problème. Cela devient encore plus complexe lorsque vous mélangez des lettres avec des chiffres dans les chaînes de version.
Step1: Utilisez StringTokenizer dans Java avec un point comme délimiteur)
StringTokenizer(String str, String delimiters)
ou
Vous pouvez utiliser String.split()
et Pattern.split()
, fractionner sur un point, puis convertir chaque chaîne en entier à l'aide de Integer.parseInt(String str)
Étape 2: Comparez l'entier de gauche à droite.
Fractionnez la chaîne sur "." ou quel que soit votre délimiteur, puis analysez chacun de ces jetons à la valeur entière et comparez.
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<s1Tokens.length; i++)
{
int s1Value = Integer.parseString(s1Tokens[i]);
int s2Value = Integer.parseString(s2Tokens[i]);
Integer s1Integer = new Integer(s1Value);
Integer s2Integer = new Integer(s2Value);
returnValue = s1Integer.compareTo(s2Value);
if( 0 == isEqual)
{
continue;
}
return returnValue; //end execution
}
return returnValue; //values are equal
}
Je laisserai l'autre déclaration if comme exercice.
La comparaison des chaînes de version peut être un gâchis; vous obtenez des réponses inutiles parce que la seule façon de faire fonctionner cela est d'être très précis sur votre convention de commande. J'ai vu une fonction de comparaison de version relativement courte et complète sur un article de blog , avec le code placé dans le domaine public - il n'est pas dans Java mais il devrait être simple de voir comment l'adapter.
Je diviserais le problème en deux, en formulant et en comparant. Si vous pouvez supposer que le format est correct, alors comparer uniquement la version des nombres est très simple:
final int versionA = Integer.parseInt( "01.02.00".replaceAll( "\\.", "" ) );
final int versionB = Integer.parseInt( "01.12.00".replaceAll( "\\.", "" ) );
Ensuite, les deux versions peuvent être comparées sous forme d'entiers. Le "gros problème" est donc le format, mais cela peut avoir de nombreuses règles. Dans mon cas, je viens de compléter au moins deux paires de chiffres, donc le format est toujours "99,99,99", puis je fais la conversion ci-dessus; donc dans mon cas, la logique du programme est dans le formatage, et non dans la comparaison des versions. Maintenant, si vous faites quelque chose de très spécifique et que vous pouvez faire confiance à l'origine de la chaîne de version, peut-être pouvez-vous simplement vérifier la longueur de la chaîne de version et ensuite faire la conversion int ... mais je pense que c'est une meilleure pratique de assurez-vous que le format est comme prévu.