Преобразование не-ASCII символов в английский аналог C ++ - PullRequest
0 голосов
/ 19 октября 2019

Мне нужно сравнить данные, которые были обработаны из разных мест, некоторые из которых имеют символы не ascii, особенно английские буквы с акцентами на них. Примером является «Фредерик Готье»: -61�: -87�: -61�: -87 ». Когда я посмотрел на значения int для символа, я заметил, что эти символы всегда представляют собой комбинацию из 2 «символов» со значениями -61, указывающими на то, что буква будет акцентирована, за которой следует буква, в данном случае -87 дляс акцентом 'е'. Моя цель - просто «сбросить» ударение и использовать английский символ. Очевидно, я не могу полагаться на это поведение от системы к системе, так как вы справляетесь с этой ситуацией? std :: string обрабатывает символы без проблем, но как только я добираюсь до уровня char, вот тут и возникают проблемы. Любое руководство?

#include <iostream>
#include <fstream>
#include <algorithm>

int main(int argc, char** argv){
    std::fstream fin;
    std::string line;
    std::string::iterator it;
    bool leave = false;
    fin.open(argv[1], std::ios::in);

    while(getline(fin, line)){
        std::for_each(line.begin(), line.end(), [](char &a){
            if(!isascii(a)) {
                if(int(a) == -68) a = 'u';
                else if(int(a) == -74) a = 'o';
                else if(int(a) == -83) a = 'i';
                else if(int(a) == -85) a = 'e';
                else if(int(a) == -87) a = 'e';
                else if(int(a) == -91) a = 'a';
                else if(int(a) == -92) a = 'a';
                else if(int(a) == -95) a = 'a';
                else if(int(a) == -120) a = 'n';
            }
        });
        it = line.begin();
        while(it != line.end()){
            it = std::find_if(line.begin(), line.end(), [](char &a){ return !isascii(a); });
            if(it != line.end()){
                line.erase(it);
                it = line.begin();
            }
        }
        std::cout << line << std::endl;
        std::for_each(line.begin(), line.end(), [&leave](char &a){
            if(!isascii(a)) {
                std::cout << a << " : " << int(a);
            }
        });
        if(leave){
            fin.close();
            return 1;
        }
    }
    fin.close();
    return 0;
}

1 Ответ

1 голос
/ 19 октября 2019

Это сложная задача в целом, и вам, вероятно, придется адаптировать свое решение к конкретной задаче. Для транслитерации вашей строки из любой кодировки в ASCII лучше полагаться на библиотеку, а не пытаться реализовать это самостоятельно. Вот пример использования iconv:

#include <iconv.h>
#include <memory>
#include <type_traits>
#include <string>
#include <iostream>
#include <algorithm>
#include <string_view>
#include <cassert>
using namespace std;

string from_u8string(const u8string &s) {
  return string(s.begin(), s.end());
}

using iconv_handle = unique_ptr<remove_pointer<iconv_t>::type, decltype(&iconv_close)>;
iconv_handle make_converter(string_view to, string_view from) {
    auto raw_converter = iconv_open(to.data(), from.data());
    if (raw_converter != (iconv_t)-1) {
        return { raw_converter, iconv_close };
    } else {
        throw std::system_error(errno, std::system_category());
    }
}

string convert_to_ascii(string input, string_view encoding) {
    iconv_handle converter = make_converter("ASCII//TRANSLIT", encoding);

    char* input_data = input.data();
    size_t input_size = input.size();

    string output;
    output.resize(input_size * 2);
    char* converted = output.data();
    size_t converted_size = output.size();

    auto chars_converted = iconv(converter.get(), &input_data, &input_size, &converted, &converted_size);
    if (chars_converted != (size_t)(-1)) {
        return output;
    } else {
        throw std::system_error(errno, std::system_category());
    }
}

string convert_to_plain_ascii(string_view input, string_view encoding) {
    auto converted = convert_to_ascii(string{ input }, encoding);
    converted.erase(
        std::remove_if(converted.begin(), converted.end(), [](char c) { return !isalpha(c); }),
        converted.end()
    );
    return converted;
}

int main() {
    try {
        auto converted_utf8 = convert_to_plain_ascii(from_u8string(u8"Frédérik"), "UTF-8");
        assert(converted_utf8 == "Frederik");
        auto converted_1252 = convert_to_plain_ascii("Frédérik", "windows-1252");
        assert(converted_1252 == "Frederik");
    } catch (std::system_error& e) {
        cout << "Error " << e.code() << ": " << e.what() << endl;
    }
}
...