排序上的绳,可能含有一些
-
01-07-2019 - |
题
我需要写一Java比较的类比较弦,然而有一种扭曲。如果两个串它是比较相同的开始和结束的串相同,而中间部分的不同之处是整数,则进行比较的基础上数值的那些整数。例如,我想以下的串结束,以便他们显示:
- aaa
- bbb3ccc
- bbb12ccc
- ccc11
- ddd
- eee3ddd jpeg2000eee
- eee12ddd jpeg2000eee
正如你可以看到,可能还有其他整数字符串中,所以我不能仅仅使用经常表达的打破任何整数。我想只是走的串从一开始直到我找到一点,不匹配,然后走在从结束直到我找到一点,不匹配,然后对比的位中经常表达"[0-9]+",并且如果进行比较,然后在做一个数字比较,否则做一词比较。
是否有一个更好的办法?
更新 我不认为我可以保证,其它数字符串中,那些可能匹配,没有空间周围的人,或那些不同的做有空间。
解决方案
其他提示
有趣的小挑战中,我解决它。
这里是我需要的问题:
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());
这个算法需要多更多的试验,但它似乎表现得相当好。
[编辑]我加入了一些更多的评论意见应更加清晰。我看到有更多的答案比当我开始代码这个...但我希望,我提供了一个良好的起基地和/或某些想法。
执行我在这里提出简单和有效的。它没有分配任何额外存储器,直接或间接地通过使用经常表达形式或方法,例如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为可移植性。但是真正的"人"的分类有很多其他的怪癖像"披头士"被排为"披头士乐队的"。没有简单的解决方案,以这个一般问题。