Domanda

Sto ordinando le stringhe che comprendono testo e numeri. Voglio che l'ordinamento ordini le parti numeriche come numeri, non alfanumerici.

Ad esempio, voglio: abc1def, ..., abc9def, abc10def

invece di: abc10def, abc1def, ..., abc9def

Qualcuno conosce un algoritmo per questo (in particolare in c ++)

Grazie

È stato utile?

Soluzione

Ho posto a questa domanda esatta (sebbene in Java) e viene indicato http://www.davekoelle.com/alphanum.html quale ha un algoritmo e le sue implementazioni in molte lingue.

Altri suggerimenti

Questo è noto come ordinamento naturale. C'è un algoritmo qui che sembra promettente.

Fai attenzione ai problemi con caratteri non ASCII (vedi Jeff's post di blog sull'argomento).

Sono disponibili diverse implementazioni di ordinamento naturale per C ++. Una breve recensione:

  • natural_sort<> - basato su Boost.Regex.
    • Nei miei test, è circa 20 volte più lento di altre opzioni.
  • Dira Jagdmann alnum.hpp , basato sul algoritmo alfanumerico
    • Potenziali problemi di overlow dei numeri interi per valori superiori a MAXINT
  • natsort di Martin Pool - scritto in C, ma banalmente utilizzabile da C ++.
    • L'unica implementazione C / C ++ che ho visto per offrire una versione senza distinzione tra maiuscole e minuscole, che sembrerebbe essere una priorità per una " natural " sorta.
    • Come le altre implementazioni, in realtà non analizza i punti decimali, ma fa zeri iniziali maiuscoli (qualsiasi cosa con uno 0 iniziale è considerata una frazione), che è un po 'strano ma potenzialmente utile.
    • PHP utilizza questo algoritmo.

Ripubblicazione parziale un'altra mia risposta :

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() funzione:

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

Utilizzo:

std::vector<std::string> str;
str.push_back("abc1def");
str.push_back("abc10def");
...
std::sort(str.begin(), str.end(), compareNat);

Per risolvere ciò che è essenzialmente un problema di analisi di una macchina a stati (ovvero automa a stati finiti ) è la strada da percorrere. Insoddisfatto delle soluzioni di cui sopra, ho scritto un semplice algoritmo di salvataggio anticipato one-pass che batte le varianti C / C ++ suggerite sopra in termini di prestazioni, non soffre di errori di overflow del tipo di dati numerici ed è facile da modificare per aggiungere insensibilità al caso, se necessario .

Le fonti sono disponibili qui

// -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;
}

Non sono sicuro che tu voglia, comunque, puoi provare :-)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top