簡體   English   中英

非ASCII字符的字典排序

[英]Lexicographical sorting for non-ascii characters

我通過以下代碼對 ascii 字符進行了字典排序:

std::ifstream infile;
std::string line, new_line;
std::vector<std::string> v;
while(std::getline(infile, line))
            {
                // If line is empty, ignore it
                if(line.empty())
                    continue;
                new_line = line + "\n";
                // Line contains string of length > 0 then save it in vector
                if(new_line.size() > 0)
                    v.push_back(new_line);
            }   
sort(v.begin(), v.end());

結果應該是: aahr abyutrw bb bhehjr cgh cuttrew....

但我不知道如何按如下順序對 ascii 和非 ascii 字符進行字典排序:a A À Á Ã brg Baq ckrwg CkfgF d Dgrn ... 請告訴我如何為它編寫代碼。 謝謝!

OP 沒有,但我覺得值得一提:說到非 ASCII 字符,也應該考慮編碼。

每個軟件開發人員絕對、肯定必須了解 Unicode 和字符集的絕對最小值(沒有借口!)

À、Á 和 Â 等字符不是7 位 ASCII的一部分,但在各種 8 位編碼中被考慮在內,例如Windows 1252 因此,不允許某個字符(不是 ASCII 的一部分)在任何編碼中具有相同的代碼點(即數字)。 (大多數字符在大多數編碼中都沒有數字。)

但是, Unicode提供了一個唯一的編碼表,其中包含任何其他編碼的所有字符(我相信)。 有如下實現

  • UTF-8其中代碼點由 1 個或多個 8 位值表示(使用char存儲)
  • UTF-16 ,其中代碼點用 1 個或 2 個 16 位值表示(使用std::char16_twchar_t存儲)
  • UTF-32 ,其中代碼點用 1 個 32 位值表示(使用std::char32_t存儲,或者,如果它有足夠的大小,可能使用wchar_t存儲)。

關於wchar_t的大小: 字符類型

話雖如此,我在示例中使用了wchar_tstd::wstring來使變音符號語言環境和平台的使用獨立。


std::sort()中用於對一系列T元素進行排序的順序默認定義為
bool < operator(const T&, const T&) T <運算符。
但是,有一些std::sort()的風格來定義自定義謂詞。

自定義謂詞必須匹配簽名並且必須提供嚴格的弱排序關系

因此,我建議使用std::map將字符映射到導致預期順序的索引。

這是我在示例中使用的謂詞:

  // sort words
  auto charIndex = [&mapChars](wchar_t chr)
  {
    const CharMap::const_iterator iter = mapChars.find(chr);
    return iter != mapChars.end()
      ? iter->second
      : (CharMap::mapped_type)mapChars.size();
  };

  auto pred
    = [&mapChars, &charIndex](const std::wstring &word1, const std::wstring &word2)
  {
    const size_t len = std::min(word1.size(), word2.size());
    // + 1 to include zero terminator
    for (size_t i = 0; i < len; ++i) {
      const wchar_t chr1 = word1[i], chr2 = word2[i];
      const unsigned i1 = charIndex(chr1), i2 = charIndex(chr2);
      if (i1 != i2) return i1 < i2;
    }
    return word1.size() < word2.size();
  };

  std::sort(words.begin(), words.end(), pred);

從下到上:

  1. std::sort(words.begin(), words.end(), pred); 使用第三個參數調用,該參數為我的自定義訂單提供謂詞pred
  2. lambda pred()逐字符比較兩個std::wstring 因此,比較是使用std::map mapChars的,它將wchar_t映射到unsigned即字符到我的順序中的等級。
  3. mapChars僅存儲所有字符值的選擇。 因此,在mapChars中可能找不到 quest 中的角色。 為了處理這個問題,使用了一個幫助程序 lambda charIndex() ,它在這種情況下返回mapChars.size() - 它被授予高於所有出現的索引。

CharMap類型只是一個typedef

typedef std::map<wchar_t, unsigned> CharMap;

要初始化CharMap ,使用 function:

CharMap makeCharMap(const wchar_t *table[], size_t size)
{
  CharMap mapChars;
  unsigned rank = 0;
  for (const wchar_t **chars = table; chars != table + size; ++chars) {
    for (const wchar_t *chr = *chars; *chr; ++chr) mapChars[*chr] = rank;
    ++rank;
  }
  return mapChars;
}

必須使用包含按預期順序的所有字符組的字符串數組來調用它:

const wchar_t *table[] = {
  L"aA", L"äÄ", L"bB", L"cC", L"dD", L"eE", L"fF", L"gG", L"hH", L"iI", L"jJ", L"kK", L"lL", L"mM", L"nN",
  L"oO", L"öÖ", L"pP", L"qQ", L"rR", L"sS", L"tT", L"uU", L"üÜ", L"vV", L"wW", L"xX", L"yY", L"zZ"
};

完整樣本:

#include <string>
#include <sstream>
#include <vector>

static const wchar_t *table[] = {
  L"aA", L"äÄ", L"bB", L"cC", L"dD", L"eE", L"fF", L"gG", L"hH", L"iI", L"jJ", L"kK", L"lL", L"mM", L"nN",
  L"oO", L"öÖ", L"pP", L"qQ", L"rR", L"sS", L"tT", L"uU", L"üÜ", L"vV", L"wW", L"xX", L"yY", L"zZ"
};

static const wchar_t *tableGerman[] = {
  L"aAäÄ", L"bB", L"cC", L"dD", L"eE", L"fF", L"gG", L"hH", L"iI", L"jJ", L"kK", L"lL", L"mM", L"nN",
  L"oOöÖ", L"pP", L"qQ", L"rR", L"sS", L"tT", L"uUüÜ", L"vV", L"wW", L"xX", L"yY", L"zZ"
};

typedef std::map<wchar_t, unsigned> CharMap;

// fill a look-up table to map characters to the corresponding rank
CharMap makeCharMap(const wchar_t *table[], size_t size)
{
  CharMap mapChars;
  unsigned rank = 0;
  for (const wchar_t **chars = table; chars != table + size; ++chars) {
    for (const wchar_t *chr = *chars; *chr; ++chr) mapChars[*chr] = rank;
    ++rank;
  }
  return mapChars;
}

// conversion to UTF-8 found in https://stackoverflow.com/a/7561991/7478597
// needed to print to console
// Please, note: std::codecvt_utf8() is deprecated in C++17. :-(
std::wstring_convert<std::codecvt_utf8<wchar_t>> utf8_conv;

// collect words and sort accoring to table
void printWordsSorted(
  const std::wstring &text, const wchar_t *table[], const size_t size)
{
  // make look-up table
  const CharMap mapChars = makeCharMap(table, size);
  // strip punctuation and other noise
  std::wstring textClean;
  for (const wchar_t chr : text) {
    if (chr == ' ' || mapChars.find(chr) != mapChars.end()) {
      textClean += chr;
    }
  }
  // fill word list with sample text
  std::vector<std::wstring> words;
  for (std::wistringstream in(textClean);;) {
    std::wstring word;
    if (!(in >> word)) break; // bail out
    // store word
    words.push_back(word);
  }
  // sort words
  auto charIndex = [&mapChars](wchar_t chr)
  {
    const CharMap::const_iterator iter = mapChars.find(chr);
    return iter != mapChars.end()
      ? iter->second
      : (CharMap::mapped_type)mapChars.size();
  };
  auto pred
    = [&mapChars, &charIndex](const std::wstring &word1, const std::wstring &word2)
  {
    const size_t len = std::min(word1.size(), word2.size());
    // + 1 to include zero terminator
    for (size_t i = 0; i < len; ++i) {
      const wchar_t chr1 = word1[i], chr2 = word2[i];
      const unsigned i1 = charIndex(chr1), i2 = charIndex(chr2);
      if (i1 != i2) return i1 < i2;
    }
    return word1.size() < word2.size();
  };
  std::sort(words.begin(), words.end(), pred);
  // remove duplicates
  std::vector<std::wstring>::iterator last = std::unique(words.begin(), words.end());
  words.erase(last, words.end());
  // print result
  for (const std::wstring &word : words) {
    std::cout << utf8_conv.to_bytes(word) << '\n';
  }
}

template<typename T, size_t N>
size_t size(const T (&arr)[N]) { return sizeof arr / sizeof *arr; }

int main()
{
  // a sample string
  std::wstring sampleText
    = L"In the German language the ä (a umlaut), ö (o umlaut) and ü (u umlaut)"
      L" have the same lexicographical rank as their counterparts a, o, and u.\n";
  std::cout << "Sample text:\n"
    << utf8_conv.to_bytes(sampleText) << '\n';
  // sort like requested by OP
  std::cout << "Words of text sorted as requested by OP:\n";
  printWordsSorted(sampleText, table, size(table));
  // sort like correct in German
  std::cout << "Words of text sorted as usual in German language:\n";
  printWordsSorted(sampleText, tableGerman, size(tableGerman));
}

Output:

Words of text sorted as requested by OP:
a
and
as
ä
counterparts
German
have
In
language
lexicographical
o
ö
rank
same
the
their
u
umlaut
ü
Words of text sorted as usual in German language:
ä
a
and
as
counterparts
German
have
In
language
lexicographical
o
ö
rank
same
the
their
u
ü
umlaut

coliru 現場演示

筆記:

我的初衷是用std::wcout 這對ä、ö、ü 不起作用。 因此,我查找了一種wstring轉換為 UTF-8 的簡單方法 我已經知道 coliru 支持 UTF-8。


@Phil1970提醒我忘記提及其他內容:

字符串的排序(根據“人類字典”順序)通常由std::locale提供。 std::collate提供依賴於語言環境的字符串字典順序。

語言環境起着重要作用,因為字符的順序可能因不同的語言環境而異。 std::collate文檔。 有一個很好的例子:

Default locale collation order: Zebra ar förnamn zebra ängel år ögrupp
English locale collation order: ängel ar år förnamn ögrupp zebra Zebra
Swedish locale collation order: ar förnamn zebra Zebra år ängel ögrupp

UTF-16 ⇔ UTF-32 ⇔ UTF-8 的轉換可以僅通過位運算來實現。 對於任何其他編碼的轉換(排除 ASCII,它是 Unicode 的一個子集),我會推薦一個像libiconv這樣的庫。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM