Как реализовать алгоритм естественной сортировки в C ++? - PullRequest
17 голосов
/ 13 марта 2009

Я сортирую строки, которые состоят из текста и чисел. Я хочу, чтобы сортировка сортировала числовые части по номерам, а не по буквам и цифрам.

Например, я хочу: abc1def, ..., abc9def, abc10def

вместо: abc10def, abc1def, ..., abc9def

Кто-нибудь знает алгоритм для этого (в частности, в C ++)

Спасибо

Ответы [ 6 ]

17 голосов
/ 13 марта 2009

Я задал этот точный вопрос (хотя в Java) и получил указание на http://www.davekoelle.com/alphanum.html, в котором есть алгоритм и его реализации на многих языках.

6 голосов
/ 13 марта 2009

Это известно как естественная сортировка. Здесь есть алгоритм здесь , который выглядит многообещающе.

Остерегайтесь проблем с символами, отличными от ASCII (см. запись в блоге Джеффа по теме).

5 голосов
/ 05 ноября 2014

Доступно несколько реализаций естественной сортировки для C ++. Краткий обзор:

  • natural_sort<> - на основе Boost.Regex.
    • В моих тестах это примерно в 20 раз медленнее, чем другие варианты.
  • Дирк Джагдманна alnum.hpp, основанный на алгоритме Дэйва Келла alphanum
    • Потенциальные проблемы целочисленного переполнения для значений, превышающих MAXINT
  • Мартин Пул natsort - написано на C, но тривиально применимо из C ++.
    • Единственная реализация C / C ++, которую я видел, предлагает версию без учета регистра, которая, по-видимому, имеет высокий приоритет для "естественной" сортировки.
    • Как и в других реализациях, он на самом деле не анализирует десятичные точки, но он выполняет начальные нули в особом случае (все, что с начальным 0, считается дробной), что немного странно, но потенциально полезно.
    • PHP использует этот алгоритм.
1 голос
/ 15 ноября 2015

Частично репост мой другой ответ :

bool compareNat(const std::string& a, const std::string& b){
    if (a.empty())
        return true;
    if (b.empty())
        return false;
    if (std::isdigit(a[0]) && !std::isdigit(b[0]))
        return true;
    if (!std::isdigit(a[0]) && std::isdigit(b[0]))
        return false;
    if (!std::isdigit(a[0]) && !std::isdigit(b[0]))
    {
        if (a[0] == b[0])
            return compareNat(a.substr(1), b.substr(1));
        return (toUpper(a) < toUpper(b));
        //toUpper() is a function to convert a std::string to uppercase.
    }

    // Both strings begin with digit --> parse both numbers
    std::istringstream issa(a);
    std::istringstream issb(b);
    int ia, ib;
    issa >> ia;
    issb >> ib;
    if (ia != ib)
        return ia < ib;

    // Numbers are the same --> remove numbers and recurse
    std::string anew, bnew;
    std::getline(issa, anew);
    std::getline(issb, bnew);
    return (compareNat(anew, bnew));
}

toUpper() функция:

std::string toUpper(std::string s){
    for(int i=0;i<(int)s.length();i++){s[i]=toupper(s[i]);}
    return s;
    }

Использование:

std::vector<std::string> str;
str.push_back("abc1def");
str.push_back("abc10def");
...
std::sort(str.begin(), str.end(), compareNat);
0 голосов
/ 29 декабря 2015

Чтобы решить, по сути, проблему синтаксического анализа, нужно выбрать конечный автомат (он же конечный автомат ). Неудовлетворенный вышеуказанными решениями, я написал простой однопроходный алгоритм раннего спасения, который превосходит предложенные выше варианты C / C ++ с точки зрения производительности, не страдает от ошибок переполнения числового типа данных и его легко модифицировать, чтобы добавить нечувствительность к регистру, если требуется .

источники можно найти здесь

0 голосов
/ 09 января 2011
// -1: s0 < s1; 0: s0 == s1; 1: s0 > s1
static int numericCompare(const string &s0, const string &s1) {
    size_t i = 0, j = 0;
    for (; i < s0.size() && j < s1.size();) {
        string t0(1, s0[i++]);
        while (i < s0.size() && !(isdigit(t0[0]) ^ isdigit(s0[i]))) {
            t0.push_back(s0[i++]);
        }
        string t1(1, s1[j++]);
        while (j < s1.size() && !(isdigit(t1[0]) ^ isdigit(s1[j]))) {
            t1.push_back(s1[j++]);
        }
        if (isdigit(t0[0]) && isdigit(t1[0])) {
            size_t p0 = t0.find_first_not_of('0');
            size_t p1 = t1.find_first_not_of('0');
            t0 = p0 == string::npos ? "" : t0.substr(p0);
            t1 = p1 == string::npos ? "" : t1.substr(p1);
            if (t0.size() != t1.size()) {
                return t0.size() < t1.size() ? -1 : 1;
            }
        }
        if (t0 != t1) {
            return t0 < t1 ? -1 : 1;
        }
    }
    return i == s0.size() && j == s1.size() ? 0 : i != s0.size() ? 1 : -1;
}

Я не очень уверен, если это вы хотите, в любом случае, вы можете попробовать: -)

...