티스토리 뷰

[원본 - 2014년 이글루스]

Image 등을 저장할 때, 파일이름으로 숫자 형식을 많이 사용한다

일반적으로 파일이름 Sorting에는 문자열 비교가 들어가기 때문에

의도하지 않은 Sorting 결과가 나오기도 한다

예> test1.txt, test10.txt, test100.txt, test2.txt, test20.txt, test3.txt ..... (원하는 결과는 test1.txt, test2.txt, test3.txt ....)

최신 버전의 윈도우 탐색기의 경우 숫자를 고려하여 Sorting이 되도록 되어있는데

만일 저걸 직접 구현하고자 한다면? 어떻게 해야 할까?

정렬에 문자열 비교함수만 바꿔주면 되겠거니 생각해서

한번 만들어 보았다

#include <stdlib.h>
#include <ctype.h>

long compare_alnum( const char *szData1, const char *szData2 )
{
    long diff;
    while( *szData1 && *szData2 ) {
        if( isdigit( *szData1 ) && isdigit( *szData2 ) ) {
            diff = strtol( szData1, (char**)&szData1, 10 )
            - strtol( szData2, (char**)&szData2, 10 );
        }
        else {
            diff = tolower( *szData1 ) - tolower( *szData2 );
        }
        
        if( diff != 0 ) {
            return diff;
        }
        
        szData1++;
        szData2++;
    }
    
    return *szData1 - *szData2;
}

소스를 보면 알겠지만, 대/소문자를 무시하고 비교한다 또한 숫자는 long형이기 때문에, 9자리 정도까지는 무리없이 처리할 수 있다

만일 더 높은 많은 자리의 숫자를 원한다면 64비트 버전으로... long형을 long long 형으로 바꾸고, 

strtol 함수를 strtoll 함수로 바꾸면 된다 (윈도우의 경우 __int64 와 _strtoi64)

덧붙여 strtol 함수에 사용된 2번째 패러미터 (char **)&szData1 이렇게 억지로 casting 해버리는건 사실 좋지 않은 코딩 방법이니 알아둡시다

sorting 할 때 문자열 비교 시, strcmp 대신 사용하시면 됩니다



[추가 내용 - 2014/03/07 18:00]

만드는김에 자바 버전도 만들어봤는데...

C버전과는 다르게 좀더 최적화를 할 수 있지 않을까 싶기도 하고...

암튼 코드 설명 드리자면

문자열을 특정 단위 (chunk) 로 분리 -> 여기서는 문자열 과 숫자

숫자면 숫자비교, 아니면 단순 문자 비교로

public class AlnumCompare implements java.util.Comparator<String> {
    private enum CHUNK_TYPE { CHUNK_ALPHA, CHUNK_DIGIT };
    
    private static class ALNUM_CHUNK
    {
        public CHUNK_TYPE type;
        public String data;
        
        public ALNUM_CHUNK( String _data, CHUNK_TYPE _type ) {
            data = _data;
            type = _type;
        }
    }
    
    public ALNUM_CHUNK getChunk( String origin, int from, int end ) {
        if( from >= end ) return null;    ///> Throw Exception Generally
        
        boolean isDigit = Character.isDigit( origin.charAt(from) );
        StringBuffer buf = new StringBuffer();
        char ch;
        
        while( from < end ) {
            ch = origin.charAt(from);
            if( isDigit != Character.isDigit(ch) ) {
                break;
            }
            buf.append( ch );
            from++;
        }
        
        return new ALNUM_CHUNK( buf.toString(), isDigit ? CHUNK_TYPE.CHUNK_DIGIT : CHUNK_TYPE.CHUNK_ALPHA );
    }
    
    @Override
    public int compare( String str1, String str2 )
    {
        int diff = 0;
        
        int i = 0,
        j = 0;
        
        int len1 = str1.length(),
        len2 = str2.length();
        
        ALNUM_CHUNK chunk1 = null,
        chunk2 = null;
        
        while( i < len1 && j < len2 ) {
            
            chunk1 = getChunk( str1, i, len1 );
            chunk2 = getChunk( str2, j, len2 );
            
            if( chunk1.type == CHUNK_TYPE.CHUNK_DIGIT && chunk2.type == CHUNK_TYPE.CHUNK_DIGIT ) {
                diff = Integer.parseInt( chunk1.data ) - Integer.parseInt( chunk2.data );
            }
            else {
                diff = chunk1.data.compareToIgnoreCase( chunk2.data );
            }
            
            if( diff != 0 ) {
                return diff;
            }
            
            i += chunk1.data.length();
            j += chunk2.data.length();
        }
        
        return len1 - len2;
    }
}


댓글
최근에 올라온 글
Total
Today
Yesterday
최근에 달린 댓글
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31