目錄
1.前言
字符串比較大小對於任何一個程序員來說並不陌生,對於一個Java程序員,我們知道使用String#compareTo(str)或者是String#compareToIgnoreCase(str)進行比較,後者是忽略大小寫。正是因爲這是一個很簡單的事情,才導致我們不假思索的去實現,然後出現了很可笑卻有時候很無奈的事情。比如,這裏有兩個字符串,分別是【S5】和【S10】,按照我們正常的邏輯來說【S10】應該排在後面,然而當我們去調用String#compareTo(str)方法的時候卻事與願違。而在實際的過程中,還有比這更復雜的字符串,追求根本還是數字給我們惹得禍。那麼,筆者就通過兩個在工作中遇到的例子聊一下工作中字符串比較的那些事。
2.案例一
2.1 需求描述
現在給定兩個字符串,我們姑且認爲是元器件的型號規格。我們需要對齊進行比較排序。在比較過程中,如果出現數字,需要根據數字的大小進行排序,而不是字符串排序。比如,現在兩個字符串分別爲【XDE100RTE】和【XDE15RGF】。我們看到這兩個字符串前綴都相等,爲【XDE】;但是後面的卻發生了區別,第一個字符串數字爲【100】,第二個字符串數字爲【15】,如果我們單純的進行比較,那結果會差之千里。所以我們需要對此比較進行特殊處理。
2.2 需求分析
通過上面的示例我們發現,兩個字符前面的多個字符串都相等,所以我們可以按照字符串的比較進行,當發現當前位的字符爲數字,我們需要將此爲左右相鄰的字符串都提取到,進行數學比較,進而返回結果。
2.3 代碼實現1
public static int compareStr(String str1,String str2){
str1 = str1 == null ? "" : str1;
str2 = str2 == null ? "" : str2;
if (str1.equals(str2)) {
return 0;
}
//獲取兩個字符串較短長度
int n = Math.min(str1.length(), str2.length());
int result = 0;
int position = 0;
int numPosition = -1;
for (; position < n; position++) {
int int1 = str1.charAt(position);
int int2 = str2.charAt(position);
if(int1 >= 48 && int1 <= 57 && int2 >= 48 && int2 <= 57){
if(numPosition == -1){
numPosition = position;
}
}else{
numPosition = -1;
}
result = int1 - int2;
if(result == 0) {
continue;
}
if(numPosition == -1){
return result;
}
break;
}
String compare1 = str1.substring(numPosition).replaceAll("(\\d+)[^\\d].*","$1");
String compare2 = str2.substring(numPosition).replaceAll("(\\d+)[^\\d].*","$1");
//這裏正則表達式保證轉換字符串爲數字肯定成功,
return Integer.valueOf(compare1) - Integer.valueOf(compare2);
}
2.4 代碼實現2
public static int compare2(String s1, String s2) {
s1 = s1 == null ? "" : s1;
s2 = s2 == null ? "" : s2;
if (s1.equals(s2)) {
return 0;
}
if (s1.matches("^\\d.*") ^ s2.matches("^\\d.*")) {
return s1.compareTo(s2);
}
String[] s1Arr = s1.replaceAll("(\\d+)", "``$1``").split("``");
String[] s2Arr = s2.replaceAll("(\\d+)", "``$1``").split("``");
int n = Math.min(s1Arr.length, s2Arr.length);
for (int i = 0; i < n; i++) {
String s1Str = s1Arr[i];
String s2Str = s2Arr[i];
int result = compares(s1Str, s2Str);
if (result != 0) {
return result;
}
}
return s1Arr.length - s2Arr.length;
}
private static int compares(String s1Str, String s2Str) {
if (!s1Str.matches("\\d+") && !s2Str.matches("\\d+")) {
return s1Str.compareTo(s2Str);
}
return Integer.valueOf(s1Str) - Integer.valueOf(s2Str);
}
2.5 小結
其實還是有些小鬱悶的,代碼1是晚上寫出來的,代碼2是之前寫的。今天晚上還是有一些成就的。還是
3.案例二
3.1 需求描述
現在給定一個字符串,這是一個由N個子串以“,”分割的字符串,其中每個子串是以一個或者多個字符與數字拼接而成,而且每個子串不會有重複。這麼說的有些抽象,我們舉一個簡單的例子。例如,"C3,C1,AB5,C2,AB1,C10,C6,C9,C11,C21,AB3,AB4"。現在需要對其進行如下處理:
1)前綴相同的要放到一起,按照上面的字符串,應該把C開頭的放到一起,AB開頭的放到一起。
2)對於開頭字符相同的按照子串數字部分進行排序,這裏需要注意不能將C9排到C10後面。
3)對於子串後綴數字存在三個以及以上連續的排序,要用"~"進行合併。例如上面的字符串,包含【C9,C10,C11】,所以應該寫成C9~C11。
好了,需要說完了。是否感覺有些暈。那麼我們現在就開始分析了。
3.2 需求分析
1)首先我們檢查給定的字符串是否滿足我們的要求,如果不滿足就直接退出。
2)爲了方便處理,需要定義一個對象TagNum,對象記錄每個子串的前綴和後綴編號。
3)定義TagNum的相鄰規則,前綴相同,後綴差1定義爲相鄰,其他情況定義爲不相鄰。
4)計算當前值和後一個值的相鄰關係,並記錄到當前值
5)拼接字符串
這裏涉及到一個邏輯,我們可以捋一下。我們先從第二個元素開始,和前面的相鄰關係進行比較。
所以我們有邏輯:
1)當前一個爲0(false),應該追加【,當前值】;
2)當前一個值爲1(true),當前值爲1,跳過本次循環;
3)當前一個值爲1(true),當前值爲0,應該追加至【~當前值】
3.3 代碼實現
public class StringUtil {
public String changeOccur(String str){
if(null == str || "".equals(str)){
return "";
}
//驗證字符串是否滿足規則
String reg = "([A-Za-z]+\\d+,)*[A-Za-z]+\\d+";
if(!str.matches(reg)){
String msg = String.format("傳入字符%s串不符合要求", str);
throw new RuntimeException(msg);
}
String[] occuArr = str.split(",");
//轉換成數組
Set<TagNum> tagSet = new TreeSet();
for (int i = 0; i < occuArr.length; i++) {
tagSet.add(new TagNum(occuArr[i].trim()));
}
List<TagNum> tagList = new ArrayList<>(tagSet);
List<Boolean> tagBool = new ArrayList<>();
for (int i = 0; i < tagList.size()-1; i++) {
tagBool.add(tagList.get(i).adjoin(tagList.get(i+1)));
}
tagBool.add(false);
StringBuilder resultSb = new StringBuilder();
resultSb.append(tagList.get(0));
for (int i = 1; i < tagBool.size(); i++) {
if(!tagBool.get(i-1)){
resultSb.append(",").append(tagList.get(i));
continue;
}
if(!tagBool.get(i)){
resultSb.append("~").append(tagList.get(i));
}
}
return resultSb.toString();
}
private class TagNum implements Comparable<TagNum>{
private String pre;
private Integer num;
private TagNum(String tagNum){
String pre = tagNum.replaceAll("([A-Za-z]+)\\d+","$1");
String numStr = tagNum.substring(pre.length());
this.pre = pre;
this.num = Integer.valueOf(numStr);
}
public boolean adjoin(TagNum otherTagNum){
if(pre.equalsIgnoreCase(otherTagNum.getPre())){
return otherTagNum.getNum()- num == 1;
}else{
return false;
}
}
public String getPre() {
return pre;
}
public void setPre(String pre) {
this.pre = pre;
}
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
@Override
public int compareTo(TagNum o) {
int result = getPre().compareToIgnoreCase(o.getPre());
if(result != 0){
return result;
}
return getNum() - o.getNum();
}
@Override
public String toString() {
return pre + num;
}
}
}