2010-05-06 6 views
8

나는 인간이 그들을 분류하는 방식으로 영숫자 문자열을 정렬하고 싶습니다. 즉, "A2"는 "A10"앞에오고 "a"는 확실히 "Z"앞에옵니다! 미니 파서를 작성하지 않고 할 수있는 방법이 있습니까? 이상적으로 "A1B1"앞에 "A1B1"을 넣을 수도 있습니다. 가능한 답변으로 "Natural (human alpha-numeric) sort in Microsoft SQL 2005"이라는 질문이 표시되지만 "Sorting Strings for Humans with IComparer"처럼 다양한 라이브러리 기능을 사용합니다.C++ 문자열은 인간과 비슷합니까?

#include <set> 
#include <iterator> 
#include <iostream> 
#include <vector> 
#include <cassert> 

template <typename T> 
struct LexicographicSort { 
    inline bool operator() (const T& lhs, const T& rhs) const{ 
    std::ostringstream s1,s2; 
    s1 << toLower(lhs); s2 << toLower(rhs); 
    bool less = s1.str() < s2.str(); 
    //Answer: bool less = doj::alphanum_less<std::string>()(s1.str(), s2.str()); 
    std::cout<<s1.str()<<" "<<s2.str()<<" "<<less<<"\n"; 
    return less; 
    } 

    inline std::string toLower(const std::string& str) const { 
    std::string newString(""); 
    for (std::string::const_iterator charIt = str.begin(); 
     charIt!=str.end();++charIt) { 
      newString.push_back(std::tolower(*charIt)); 
     } 
     return newString; 
     } 
}; 


int main(void) { 
    const std::string reference[5] = {"ab","B","c1","c2","c10"}; 
    std::vector<std::string> referenceStrings(&(reference[0]), &(reference[5])); 

    //Insert in reverse order so we know they get sorted 
    std::set<std::string,LexicographicSort<std::string> > strings(referenceStrings.rbegin(), referenceStrings.rend()); 

    std::cout<<"Items:\n"; 
    std::copy(strings.begin(), strings.end(), std::ostream_iterator<std::string>(std::cout, "\n")); 
    std::vector<std::string> sortedStrings(strings.begin(), strings.end()); 
    assert(sortedStrings == referenceStrings); 
} 
+0

'집합'을 사용하는 이유가 '벡터'뿐만 아니라 '정렬'이 아닌가? –

+3

첫째, A1B2가 A2B1에 비해 어떻게 정렬됩니까? 나는 이것을 해본 적이 없지만, 아마도 당신의 끈을 덩어리로 깨기 시작할 것입니다. 텍스트, 숫자, 텍스트, 숫자 등. 그런 다음 숫자 비트가 문자열이 아닌 숫자로 정렬된다는 이해와 함께 여러 멤버가있는 다른 데이터 구조와 동일한 방식으로 정렬합니다. –

+0

@ 형제 : 특별한 이유가 없습니다. @Zickefoose : 나는 A1B2, A1B10, A2B1과 같이 (오름차순) 정렬 할 것입니다. 나는 당신이 원시적 인 렉싱 (lexing)을해야만 할 수도 있다고 생각하지만, 도움이된다면 오류가 발생하기 쉬운 것을 피하려고한다. –

답변

5

미니 파서를 작성하지 않으면 어떤 방법이 있습니까?

다른 사람이 그렇게하니?

이 구현을 사용하고 있습니다 : http://www.davekoelle.com/alphanum.html, wchar_t도 지원하도록 수정했습니다.

+0

좋아! 내가 찾고 있었던 바로는, 한번 케이스 무감각을 추가했습니다. 위의 "less"계산을 'bool less = doj :: alphanum_less () (s1.str(), s2.str());'으로 바꿉니다. 고맙습니다! –

+0

파이썬에서 natural sort를 구현하기 위해 똑같은 링크를 사용했습니다. 파이썬의 적분은 하나의 요구만큼 큽니다. –

0

미니 파서를 작성하지 않고 그것을 할 수있는 방법이 있나요 :

아래는 현재 실패한 테스트 케이스는? 나는 그 대답이 '아니오'라고 생각할 것이다. 그러나 파서를 작성하는 것은 그렇게 어렵지 않습니다. 나는 우리 회사의 주식을 분류하기 위해 얼마 전에 이것을해야했다. 기본적으로 숫자를 스캔하여 배열로 변환하십시오. 모든 문자의 "유형"을 확인하십시오 : 알파, 숫자, 어쩌면 당신은 특별한 거래를 해야하는 다른 사람이 있습니다. A-B-C가 AB-A보다 먼저 정렬되기를 원했기 때문에 나는 하이픈을 특별하게 취급해야했습니다. 그런 다음 문자를 벗겨 내기 시작하십시오. 첫 번째 문자와 동일한 유형 인 한 동일한 양동이에 들어갑니다. 유형이 변경되면 다른 버킷에 넣기 시작합니다. 그런 다음 버킷 단위로 비교하는 비교 함수가 필요합니다. 두 양동이가 모두 알파 인 경우 일반 알파 비교를 수행합니다. 둘 다 숫자 일 때 정수로 변환하고 정수 비교를 수행하거나 길이를 더 길게 또는 동등한 길이로 채 웁니다. 유형이 다른 경우 A-A가 A-1 전후에 오는 것과 같은 비교 방법에 대한 규칙이 필요합니다.

그것은 사소한 일이 아니며 발생할 수있는 모든 이상한 경우에 대한 규칙을 제시해야하지만 몇 시간 안에 해결할 수 있다고 생각합니다.

0

구문 분석이 없으면 동일한 문자열의 일부로 사람이 작성한 숫자 (첫 번째 값이 0 인 높은 값)와 일반 문자를 비교할 수 없습니다.

구문 분석은 매우 복잡 할 필요는 없습니다. 대/소문자 구분 및 특수 문자 제거와 같은 간단한 해시 테이블 ('A'= 'a'= 1, 'B'= 'b'= '2, ... 또는'A '= 1,'a ' = 2, 'B'= 3, ..., '-'= 0 (스트립)), 문자열을 해시 된 값의 배열로 다시 매핑 한 다음 숫자의 경우를 자릅니다 (숫자가 발생하고 마지막 문자가 마지막 숫자를 10으로 곱하고 현재 값을 그 숫자에 더하십시오.

정상적으로 정렬하십시오.

2

"파서"가 의미하는 바에 따라 다릅니다. 파서 작성을 피하고 싶다면 라이브러리 기능을 이용해야한다고 생각합니다.

  • 문자열을 알파벳순, 숫자 또는 "기타"부분 시퀀스로 처리하십시오.
  • isalnum을 사용하여 각 문자열의 다음 영숫자 시퀀스를 가져오고 + 또는 -의 역 추적을 숫자로 입력하십시오. strtold을 사용하여 숫자 부분 시퀀스의 끝을 찾습니다.
  • 하나가 숫자이고 하나가 영문자이면 숫자 부분 시퀀스가있는 문자열이 먼저옵니다.
  • 하나의 문자열에 문자가 부족한 경우 먼저옵니다.
  • strcoll을 사용하면 현재 로캘의 알파벳순 하위 시퀀스를 비교할 수 있습니다.
  • strtold을 사용하면 현재 로캘의 숫자 하위 시퀀스를 비교할 수 있습니다.
  • 하나 또는 두 개의 문자열로 끝날 때까지 반복하십시오.
  • 도화선을 strcmp으로 끊습니다.

이 알고리즘은 long double의 정밀도를 초과하는 숫자 문자열을 비교할 때 약점이 있습니다.