我需要写一Java比较的类比较弦,然而有一种扭曲。如果两个串它是比较相同的开始和结束的串相同,而中间部分的不同之处是整数,则进行比较的基础上数值的那些整数。例如,我想以下的串结束,以便他们显示:

  • aaa
  • bbb3ccc
  • bbb12ccc
  • ccc11
  • ddd
  • eee3ddd jpeg2000eee
  • eee12ddd jpeg2000eee

正如你可以看到,可能还有其他整数字符串中,所以我不能仅仅使用经常表达的打破任何整数。我想只是走的串从一开始直到我找到一点,不匹配,然后走在从结束直到我找到一点,不匹配,然后对比的位中经常表达"[0-9]+",并且如果进行比较,然后在做一个数字比较,否则做一词比较。

是否有一个更好的办法?

更新 我不认为我可以保证,其它数字符串中,那些可能匹配,没有空间周围的人,或那些不同的做有空间。

有帮助吗?

解决方案

该Alphanum算法

从网站

"人类串的数字不同于软件。大多数排序的算法进行比较ASCII值,其产生的排序不符合人的逻辑。这里是如何解决它。"

编辑:这里有一个链接到 Java比较执行情况 从该网站上。

其他提示

有趣的小挑战中,我解决它。

这里是我需要的问题:

String[] strs =
{
  "eee 5 ddd jpeg2001 eee",
  "eee 123 ddd jpeg2000 eee",
  "ddd",
  "aaa 5 yy 6",
  "ccc 555",
  "bbb 3 ccc",
  "bbb 9 a",
  "",
  "eee 4 ddd jpeg2001 eee",
  "ccc 11",
  "bbb 12 ccc",
  "aaa 5 yy 22",
  "aaa",
  "eee 3 ddd jpeg2000 eee",
  "ccc 5",
};

Pattern splitter = Pattern.compile("(\\d+|\\D+)");

public class InternalNumberComparator implements Comparator
{
  public int compare(Object o1, Object o2)
  {
    // I deliberately use the Java 1.4 syntax, 
    // all this can be improved with 1.5's generics
    String s1 = (String)o1, s2 = (String)o2;
    // We split each string as runs of number/non-number strings
    ArrayList sa1 = split(s1);
    ArrayList sa2 = split(s2);
    // Nothing or different structure
    if (sa1.size() == 0 || sa1.size() != sa2.size())
    {
      // Just compare the original strings
      return s1.compareTo(s2);
    }
    int i = 0;
    String si1 = "";
    String si2 = "";
    // Compare beginning of string
    for (; i < sa1.size(); i++)
    {
      si1 = (String)sa1.get(i);
      si2 = (String)sa2.get(i);
      if (!si1.equals(si2))
        break;  // Until we find a difference
    }
    // No difference found?
    if (i == sa1.size())
      return 0; // Same strings!

    // Try to convert the different run of characters to number
    int val1, val2;
    try
    {
      val1 = Integer.parseInt(si1);
      val2 = Integer.parseInt(si2);
    }
    catch (NumberFormatException e)
    {
      return s1.compareTo(s2);  // Strings differ on a non-number
    }

    // Compare remainder of string
    for (i++; i < sa1.size(); i++)
    {
      si1 = (String)sa1.get(i);
      si2 = (String)sa2.get(i);
      if (!si1.equals(si2))
      {
        return s1.compareTo(s2);  // Strings differ
      }
    }

    // Here, the strings differ only on a number
    return val1 < val2 ? -1 : 1;
  }

  ArrayList split(String s)
  {
    ArrayList r = new ArrayList();
    Matcher matcher = splitter.matcher(s);
    while (matcher.find())
    {
      String m = matcher.group(1);
      r.add(m);
    }
    return r;
  }
}

Arrays.sort(strs, new InternalNumberComparator());

这个算法需要多更多的试验,但它似乎表现得相当好。

[编辑]我加入了一些更多的评论意见应更加清晰。我看到有更多的答案比当我开始代码这个...但我希望,我提供了一个良好的起基地和/或某些想法。

伊恩*格里菲斯的Microsoft具有C#执行他的电话 自然排序.移植到Java应该是相当容易的,更易于从C呢!

更新: 似乎有一个Java例上 eekboom 不这一点,请参阅"compareNatural"和使用那作为你比较器来。

执行我在这里提出简单和有效的。它没有分配任何额外存储器,直接或间接地通过使用经常表达形式或方法,例如substring(),split(),toCharArray()等。

此实现首先在两个串的搜索的第一个字都不同,在最大速度,在不做任何特殊处理过这一点。具体数字的对比触发的,只有当这些人物都是两位数。一个副作用的这种实现是一个数字被认为大于其他的信件,相反,默认字典式的订单。

public static final int compareNatural (String s1, String s2)
{
   // Skip all identical characters
   int len1 = s1.length();
   int len2 = s2.length();
   int i;
   char c1, c2;
   for (i = 0, c1 = 0, c2 = 0; (i < len1) && (i < len2) && (c1 = s1.charAt(i)) == (c2 = s2.charAt(i)); i++);

   // Check end of string
   if (c1 == c2)
      return(len1 - len2);

   // Check digit in first string
   if (Character.isDigit(c1))
   {
      // Check digit only in first string 
      if (!Character.isDigit(c2))
         return(1);

      // Scan all integer digits
      int x1, x2;
      for (x1 = i + 1; (x1 < len1) && Character.isDigit(s1.charAt(x1)); x1++);
      for (x2 = i + 1; (x2 < len2) && Character.isDigit(s2.charAt(x2)); x2++);

      // Longer integer wins, first digit otherwise
      return(x2 == x1 ? c1 - c2 : x1 - x2);
   }

   // Check digit only in second string
   if (Character.isDigit(c2))
      return(-1);

   // No digits
   return(c1 - c2);
}

我知道你们,但你可以来看一看如何StrCmpLogicalW工作。这是什么资源管理器用于种文件。你可以看看葡萄酒的执行情况 在这里,.

分串入运行的字母和数字,因此"foo12条"变的名单("foo",12,"bar"),然后使用的名单的排序关键。这种方式的数字将订按数字顺序,不按字母顺序排列。

我想出了一个很简单的执行情况Java using regular expressions:

public static Comparator<String> naturalOrdering() {
    final Pattern compile = Pattern.compile("(\\d+)|(\\D+)");
    return (s1, s2) -> {
        final Matcher matcher1 = compile.matcher(s1);
        final Matcher matcher2 = compile.matcher(s2);
        while (true) {
            final boolean found1 = matcher1.find();
            final boolean found2 = matcher2.find();
            if (!found1 || !found2) {
                return Boolean.compare(found1, found2);
            } else if (!matcher1.group().equals(matcher2.group())) {
                if (matcher1.group(1) == null || matcher2.group(1) == null) {
                    return matcher1.group().compareTo(matcher2.group());
                } else {
                    return Integer.valueOf(matcher1.group(1)).compareTo(Integer.valueOf(matcher2.group(1)));
                }
            }
        }
    };
}

这里是它是如何工作:

final List<String> strings = Arrays.asList("x15", "xa", "y16", "x2a", "y11", "z", "z5", "x2b", "z");
strings.sort(naturalOrdering());
System.out.println(strings);

[x2a,x2b,15、xa,y11、y16,z,z,z5]

Alphanum algrothim是好的,但它并没有要求匹配的一个项目,我的工作。我需要能够进行排序负数和小数正确。这里是执行我来了。任何反馈意见,将不胜感激。

public class StringAsNumberComparator implements Comparator<String> {

    public static final Pattern NUMBER_PATTERN = Pattern.compile("(\\-?\\d+\\.\\d+)|(\\-?\\.\\d+)|(\\-?\\d+)");

    /**
     * Splits strings into parts sorting each instance of a number as a number if there is
     * a matching number in the other String.
     * 
     * For example A1B, A2B, A11B, A11B1, A11B2, A11B11 will be sorted in that order instead
     * of alphabetically which will sort A1B and A11B together.
     */
    public int compare(String str1, String str2) {
        if(str1 == str2) return 0;
        else if(str1 == null) return 1;
        else if(str2 == null) return -1;

        List<String> split1 = split(str1);
        List<String> split2 = split(str2);
        int diff = 0;

        for(int i = 0; diff == 0 && i < split1.size() && i < split2.size(); i++) {
            String token1 = split1.get(i);
            String token2 = split2.get(i);

            if((NUMBER_PATTERN.matcher(token1).matches() && NUMBER_PATTERN.matcher(token2).matches()) {
                diff = (int) Math.signum(Double.parseDouble(token1) - Double.parseDouble(token2));
            } else {
                diff = token1.compareToIgnoreCase(token2);
            }
        }
        if(diff != 0) {
            return diff;
        } else {
            return split1.size() - split2.size();
        }
    }

    /**
     * Splits a string into strings and number tokens.
     */
    private List<String> split(String s) {
        List<String> list = new ArrayList<String>();
        try (Scanner scanner = new Scanner(s)) {
            int index = 0;
            String num = null;
            while ((num = scanner.findInLine(NUMBER_PATTERN)) != null) {
                int indexOfNumber = s.indexOf(num, index);
                if (indexOfNumber > index) {
                    list.add(s.substring(index, indexOfNumber));
                }
                list.add(num);
                index = indexOfNumber + num.length();
            }
            if (index < s.length()) {
                list.add(s.substring(index));
            }
        }
        return list;
    }
}

PS。我想用java。郎。String.split()方法和使用"lookahead/回顾"保留标记,但我不能获得它的工作与经常表达我使用。

有趣的问题,这里我提出解决方案:

import java.util.Collections;
import java.util.Vector;

public class CompareToken implements Comparable<CompareToken>
{
    int valN;
    String valS;
    String repr;

    public String toString() {
    return repr;
    }

    public CompareToken(String s) {
    int l = 0;
    char data[] = new char[s.length()];
    repr = s;
    valN = 0;
    for (char c : s.toCharArray()) {
        if(Character.isDigit(c))
        valN = valN * 10 + (c - '0');
        else
        data[l++] = c;
    }

    valS = new String(data, 0, l);
    }

    public int compareTo(CompareToken b) {
    int r = valS.compareTo(b.valS);
    if (r != 0)
        return r;

    return valN - b.valN;
    }


    public static void main(String [] args) {
    String [] strings = {
        "aaa",
        "bbb3ccc",
        "bbb12ccc",
        "ccc 11",
        "ddd",
        "eee3dddjpeg2000eee",
        "eee12dddjpeg2000eee"
    };

    Vector<CompareToken> data = new Vector<CompareToken>();
    for(String s : strings)
        data.add(new CompareToken(s));
    Collections.shuffle(data);

    Collections.sort(data);
    for (CompareToken c : data)
        System.out.println ("" + c);
    }

}

之前发现这个线程,我实现了一个类似的解决方案在javascript。也许我的策略会找到你的好,尽管不同的语法。类似于上述,我分析了两个串的是比较和分裂他们两个入阵列,分割串在连续的数字。

...
var regex = /(\d+)/g,
    str1Components = str1.split(regex),
    str2Components = str2.split(regex),
...

即, 'hello22goodbye33'=>['hello',22,'再见,33];因此,你可以步行通过该阵'素之间的对string1和string2,做一些类型的胁迫(例如,被这件真的很多?), 和比较你走。

工作实例: http://jsfiddle.net/F46s6/3/

注意,我目前只支持整数的种类,虽然处理小数值不会太难的修改。

我2美分。工作很适合我。我主要使用它的文件名。

    private final boolean isDigit(char ch)
        {
            return ch >= 48 && ch <= 57;
        }


        private int compareNumericalString(String s1,String s2){

            int s1Counter=0;
            int s2Counter=0;
            while(true){
                if(s1Counter>=s1.length()){
                    break;
                }
                if(s2Counter>=s2.length()){
                    break;
                }
                char currentChar1=s1.charAt(s1Counter++);
                char currentChar2=s2.charAt(s2Counter++);
                if(isDigit(currentChar1) &&isDigit(currentChar2)){
                    String digitString1=""+currentChar1;
                    String digitString2=""+currentChar2;
                    while(true){
                        if(s1Counter>=s1.length()){
                            break;
                        }
                        if(s2Counter>=s2.length()){
                            break;
                        }

                        if(isDigit(s1.charAt(s1Counter))){
                            digitString1+=s1.charAt(s1Counter);
                            s1Counter++;
                        }

                        if(isDigit(s2.charAt(s2Counter))){
                            digitString2+=s2.charAt(s2Counter);
                            s2Counter++;
                        }

                        if((!isDigit(s1.charAt(s1Counter))) && (!isDigit(s2.charAt(s2Counter)))){
                            currentChar1=s1.charAt(s1Counter);
                            currentChar2=s2.charAt(s2Counter);
                            break;
                        }
                    }
                    if(!digitString1.equals(digitString2)){
                        return Integer.parseInt(digitString1)-Integer.parseInt(digitString2);
                    }
                }

                if(currentChar1!=currentChar2){
                    return currentChar1-currentChar2;
                }

            }
            return s1.compareTo(s2);
        }

我想你会需要做的比上一个逐字符的方式。抓住一个角色,如果它是一个数字,继续抓住,然后重新装配到符入一个单一的数字符串并将其转换成 int.重复另一串,然后才做比较。

简短的回答:基于上下文,我不能告诉这是否是仅有一些快速和肮脏的代码为个人使用,或者一个关键部分Goldman Sachs最新的内部会计软件,所以我就开说:嗯.这是一个相当时髦的排序的算法;尝试使用的东西有点小"曲折的"如果您能。

只要回答:

这两个问题,立即浮现在脑海中你的情况是性能和正确性。非正式的方式,确保它的快速,并确保你的算法是 总序.

(当然,如果你不排多于约100项,你也许可以无视这一段。) 性事项,作为速度的比较将会是最大的因素的速度排序(假定的排序的算法是"理想的"典型的列表)。在你的情况下,比较器的速度将主要取决于字符串的大小.串似乎是相当短,因此他们可能不会占尽的尺寸您的名单。

把每个字符串成一串数字符串元组和排序然后这一清单元组,作为建议在另一个答案,将会失败在你的一些情况下,因为你显然会有串用多个数字出现。

另一个问题是正确性。具体地说,如果算法你描述都不会允许>B>...>一个,那么你的排序将被不确定的。在你的情况下,我担心它可能的,虽然我不能证明这一点。考虑到一些分析的情况下,如:

  aa 0 aa
  aa 23aa
  aa 2a3aa
  aa 113aa
  aa 113 aa
  a 1-2 a
  a 13 a
  a 12 a
  a 2-3 a
  a 21 a
  a 2.3 a

虽然提出的问题java解决方案,对谁想要一个斯卡拉解决方案:

object Alphanum {

   private[this] val regex = "((?<=[0-9])(?=[^0-9]))|((?<=[^0-9])(?=[0-9]))"

   private[this] val alphaNum: Ordering[String] = Ordering.fromLessThan((ss1: String, ss2: String) => (ss1, ss2) match {
     case (sss1, sss2) if sss1.matches("[0-9]+") && sss2.matches("[0-9]+") => sss1.toLong < sss2.toLong
     case (sss1, sss2) => sss1 < sss2
   })

   def ordering: Ordering[String] = Ordering.fromLessThan((s1: String, s2: String) => {
     import Ordering.Implicits.infixOrderingOps
     implicit val ord: Ordering[List[String]] = Ordering.Implicits.seqDerivedOrdering(alphaNum)

     s1.split(regex).toList < s2.split(regex).toList
   })

}

我的问题是,我已经列出的组成一个组合的字母数字符串(例如22,C3,C5等),阿尔法串(例如,H,R等)和刚位数字(如99,45等)需要分类的订,C3,C5,22,H,R,45,99.我还有重复,需要删除所以我只能得到一次入境。

我也不只是工作有弦的,我订购的对象和使用特定领域内的目得到正确的顺序。

一个解决方案,似乎要对我的工作是:

SortedSet<Code> codeSet;
codeSet = new TreeSet<Code>(new Comparator<Code>() {

private boolean isThereAnyNumber(String a, String b) {
    return isNumber(a) || isNumber(b);
}

private boolean isNumber(String s) {
    return s.matches("[-+]?\\d*\\.?\\d+");
}

private String extractChars(String s) {
    String chars = s.replaceAll("\\d", "");
    return chars;
}

private int extractInt(String s) {
    String num = s.replaceAll("\\D", "");
    return num.isEmpty() ? 0 : Integer.parseInt(num);
}

private int compareStrings(String o1, String o2) {

    if (!extractChars(o1).equals(extractChars(o2))) {
        return o1.compareTo(o2);
    } else
        return extractInt(o1) - extractInt(o2);
}

@Override
public int compare(Code a, Code b) {

    return isThereAnyNumber(a.getPrimaryCode(), b.getPrimaryCode()) 
            ? isNumber(a.getPrimaryCode()) ? 1 : -1 
                : compareStrings(a.getPrimaryCode(), b.getPrimaryCode());
                }
            });

它借用一些代码,我在这里找到上计算器,加上一些调整,我自己得到它的工作是多么我需要它了。

由于试图为对象,需要一个比较以及去除重复,一个负忽悠我不得不用是我第一次有写我的对象树形图之前写到Treeset.这可能会影响性能一点,但鉴于该清单将是一个最大的大约80码的,它不应该是一个问题。

我有一个类似的问题,在我的字符串了空间分段内。我解决了它在这一方式:

public class StringWithNumberComparator implements Comparator<MyClass> {

@Override
public int compare(MyClass o1, MyClass o2) {
    if (o1.getStringToCompare().equals(o2.getStringToCompare())) {
        return 0;
    }
    String[] first = o1.getStringToCompare().split(" ");
    String[] second = o2.getStringToCompare().split(" ");
    if (first.length == second.length) {
        for (int i = 0; i < first.length; i++) {

            int segmentCompare = StringUtils.compare(first[i], second[i]);
            if (StringUtils.isNumeric(first[i]) && StringUtils.isNumeric(second[i])) {

                segmentCompare = NumberUtils.compare(Integer.valueOf(first[i]), Integer.valueOf(second[i]));
                if (0 != segmentCompare) {
                    // return only if uneven numbers in case there are more segments to be checked
                    return segmentCompare;
                }
            }
            if (0 != segmentCompare) {
                return segmentCompare;
            }
        }
    } else {
        return StringUtils.compare(o1.getDenominazione(), o2.getDenominazione());
    }

    return 0;
}

正如你可以看到,我们用阿帕奇直升机既.比较()和NumberUtils.主持人()作为标准的帮助。

在你给出的例子,你想要的数字比较有空间在他们周围,而其他数字不这样做,那么,为什么一个定期表达的不工作吗?

bbb 12 ccc

eee12ddd jpeg2000 eee

如果你正在写一个比较类,你应该实现自己的比较方法,会比较两个字符串通过符。这种比较的方法应该检查,如果你正在处理的字母、数字或混合类型(包括空间).你必须确定如何你想要一个混合型行动,是否数字来之前的字母字符或之后,并在空间的适应等。

在Linux上glibc提供strverscmp(),它也可以从gnulib为可移植性。但是真正的"人"的分类有很多其他的怪癖像"披头士"被排为"披头士乐队的"。没有简单的解决方案,以这个一般问题。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top