C ++ "биржевой тикер" со строками UTF-8 - PullRequest
0 голосов
/ 12 апреля 2020

Часть проекта включает в себя нечто похожее на прокручиваемый «тикер запаса», где большая строка «прокручивается» по выходной строке фиксированной ширины.

Используя C ++ 11 на Linux, концепция заключается в том, что Очистить при использовании латинских символов. Примерно так:

std::string inputString, outputString;
for (int inIdx = 0; inIdx < inputString.size(); inIdx++)
{
    // shift output one character left
    for (int i = 0; i < mOutputTextWidth - 1; i++)
        outputString[i] = outputString[i+1];

    // Append character to end of output
    if (inIdx < inputString.size())
        outputString[mTextWidth] = inputString.at(inIdx);
    sleep(1);
}

Вы получите что-то вроде:

[           ]
[          H]
[         HE]
[        HEL]
[      HELLO]
[     HELLO ]
[    HELLO  ]
[   HELLO   ]

Мне нужно, чтобы это работало для нелатинских символов UTF-8. Из того, что я прочитал, это сложная тема. В частности, std::string::at или [] возвращает символ, который разбивается на длинные символы UTF-8.

В C ++, как правильно это сделать?

Например. Японский

[              ]
[            こ]
[          こん]
[        こんば]
[      こんばん]
[    こんばんは]
[  こんばんは  ]
[ こんばんは   ]

(я знаю, что ширина глифа зависит от языка, это нормально. Я просто не могу понять, как манипулировать строками UTF-8)

Ответы [ 2 ]

0 голосов
/ 15 апреля 2020

После многочисленных предупреждений против wchar я реализовал решение на основе комментария, ссылающегося на это сообщение из rustyx . В этом подходе могут быть дыры, но пока он работает для меня при тестировании с английским / латинским и японским вводом.

(я полагаю, что приведенный ниже код работает только с UTF-8, не уверен в других унаследованных кодировках, таких как EU C -JP, SHIFT_JIS и др. c)

Обратите внимание, что symbolLength() определяет количество присутствующих кодовых точек, которое не будет совпадать с шириной экрана, поскольку отличается шириной (или нулевой шириной! ) могут присутствовать кодовые точки.

TqString::TqString(const std::string &s) { assign(s); }

TqString::TqString(const char *cs)
{
    std::string s(cs);
    assign(s);
}

TqString::TqString(size_t n, char c)
{
    std::string s(n, c);
    assign(s);
}

TqString &TqString::operator=(const std::string &s)
{
    assign(s);
    return *this;
}

// Unlike size(), this returns the number of UTF-8 code points
// in the input string

size_t
TqString::symbolLength() const
{
    int  symCount = 0;
    int skipCount = 0;

    for (int i = 0; i < size(); i++)
    {
        unsigned char c = at(i);
        if (skipCount == 0)
        {
            if (c >= 0xF0)
                skipCount = 3;
            else if (c >= 0xE0)
                skipCount = 2;
            else if (c >= 0xC0)
                skipCount = 1;
        }
        else
        {
            --skipCount;
        }

        if (skipCount > 0)
            continue;

        symCount++;
    }
    return symCount;
}

// Scan input string, skipping over 'n' symbols, and returning the last

std::string
TqString::symbolAt(off_t n) const
{
    std::string outString;
    int skipCount = 0;
    int symCount = 0;

    for (int i = 0; i < size(); i++)
    {
        unsigned char c = at(i);
        if (skipCount == 0)
        {
            outString = c;
            if (c >= 0xF0)
                skipCount = 3;
            else if (c >= 0xE0)
                skipCount = 2;
            else if (c >= 0xC0)
                skipCount = 1;
        }
        else
        {
            outString += c;
            --skipCount;
        }

        if (skipCount > 0)
            continue;


        if (symCount == n)
            break;

        symCount++;
    }

    return outString;
}

void
TqString::shiftLeft()
{
    std::string outString;
    if (size() == 0)
    {
        assign(outString);
        return;
    }

    for (int i = 1; i < symbolLength(); i++)
    {
        outString += symbolAt(i);
    }

    assign(outString);
}

// shift then append 's' to the end
void
TqString::shiftLeft(const TqString &s)
{
    shiftLeft();
    append(s);
}

std::string
TqString::str() const
{
    std::string ret(data());
    return ret;
}
0 голосов
/ 13 апреля 2020

В системах, которые изначально поддерживают Unicode (включая Linux) 1 , вы можете просто использовать стандартную поддержку C ++ и работать с wchar_t типы для обработки одной кодовой точки Юникода за раз.

Например, например:

#include <algorithm>
#include <clocale>
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>

int main()
{
    std::string inputUTF8 = "こんばんは!"; // assuming this source is stored in UTF-8

    std::setlocale(LC_ALL, "en_US.utf8"); // tell mbstowcs use want UTF-8->wchar_t conversion
    std::wcout.imbue(std::locale("en_US.utf8")); // tell std::wcout we want wchar_t->UTF-8 output

    std::vector<wchar_t> buf(inputUTF8.size() + 1); // reserve space
    int len = (int)std::mbstowcs(buf.data(), inputUTF8.c_str(), buf.size()); // convert to wchar_t
    if (len == -1) {
        std::cerr << "Invalid UTF-8 input\n"; // mbstowcs can fail
        return 1;
    }
    std::wstring out;
    for (int i = 0; i < len * 2; i++)
    {
        out.assign(std::max(0, len - i), L' '); // fill with ideographic space (U+3000) before

        out.append(buf.data(), std::max(0, i - len), std::min(len, i) - std::max(0, i - len));

        out.append(std::max(0, i - len), L' '); // fill with ideographic space after

        std::wcout << L"[" << out << L"]\n";
    }
}

Вывод :

[      ]
[     こ]
[    こん]
[   こんば]
[  こんばん]
[ こんばんは]
[こんばんは!]
[んばんは! ]
[ばんは!  ]
[んは!   ]
[は!    ]
[!     ]

Осторожно что mbstowcs и другие языковые настройки не являются поточно-ориентированными.

Другая возможность - использовать такую ​​библиотеку, как iconv .


1 К сожалению на Windows Поддержка Unicode ограничена; его wchar_t имеет длину 16 бит и фактически представляет UTF-16, поэтому программа будет работать только для кодовых точек basi c plane (которые по-прежнему включают в себя типичные символы CJK, но не унифицированные Han или другие символы выше U + FFFF). Хотя это все еще можно исправить, принимая во внимание UTF-16.

...