Получить подстроку длиной не более N байтов в строке UTF8 - PullRequest
0 голосов
/ 04 марта 2019

Я использую некоторый API, который требует, чтобы входная строка была допустимой строкой UTF8 с максимальной длиной 4096 байт.

У меня была следующая функция для обрезки дополнительных символов:

private static string GetTelegramMessage(string message)
{
    const int telegramMessageMaxLength = 4096; // https://core.telegram.org/method/messages.sendMessage#return-errors
    const string tooLongMessageSuffix = "...";

    if (message == null || message.Length <= 4096)
    {
        return message;
    }

    return message.Remove(telegramMessageMaxLength - tooLongMessageSuffix.Length) + tooLongMessageSuffix;
}

Это не сработало, потому что символы! = Байты и символы UTF16! = Символы UTF8.

Поэтому в основном мне нужно преобразовать строку C # UTF16 в строку UTF8 фиксированной длины.Я делаю

var bytes = Encoding.UTF8.GetBytes(myString);
// now I need to get first N characters with overall bytes size less than 4096 bytes

Я могу выразить свою потребность в Rust (рабочий пример ниже):

fn main() {
    let foo = format!("{}{}", "ᚠᛇᚻ᛫ᛒᛦᚦ᛫ᚠᚱᚩᚠᚢᚱ᛫ᚠᛁᚱᚪ᛫ᚷᛖᚻᚹᛦᛚᚳᚢᛗ Uppen Sevarne staþe, sel þar him þuhte", (1..5000).map(|_| '1').collect::<String>());
    println!("{}", foo.len());
    let message = get_telegram_message(&foo);
    println!("{}", message);
    println!("{}", message.chars().count()); // 4035
    println!("{}", message.len()); // 4096
}

pub fn get_telegram_message(foo: &str) -> String {
    const PERIOD: &'static str = "...";
    const MAX_LENGTH: usize = 4096;
    let message_length = MAX_LENGTH - PERIOD.len();

    foo.chars()
        .map(|c| (c, c.len_utf8())) // getting length for evey char
        .scan((0, '\0'), |(s, _), (c, size)| {
            *s += size; //  running total for all previosely seen characters
            Some((*s, c))
        })
        .take_while(|(len, _)| len <= &message_length) // taking while running total is less than maximum message size
        .map(|(_, c)| c)
        .chain(PERIOD.chars()) // add trailing ellipsis
        .collect() // building a string
}

https://play.rust -lang.org /? Version = stable & mode = debug & edition= 2018 & gist = 471ad0cbe9b0b01b50ec250d17dea233

Проблема здесь в том, что у меня нет chars() итератора в C #, который позволяет мне обрабатывать последовательность байтов как символы UTF8.

Я игралс Encoding.UTF8, но я не нашел подходящих API для выполнения этой задачи.


Связанные статьи как-то связаны с моим вопросом, но первый ответ очень плохой, второй переопределение UTF8итератор (это то, что я назвал IEnumerable<long> ниже).Поскольку я знаю, как это реализовать, мой вопрос о встроенной функции для выполнения этой задачи, так что ни один из связанных ответов не отвечает на это.

1 Ответ

0 голосов
/ 04 марта 2019

Я думаю, Encoder.Convert - это, вероятно, метод, который вам нужен.

Я интерпретировал вопрос как значение

У меня есть строка, котораябудет преобразован в байты UTF-8.Я хочу обрезать его так, чтобы его кодировка UTF-8 составляла максимум 4096 байт, но я хочу убедиться, что я не обрезал его в середине кодовой точки UTF-8.

private static string GetTelegramMessage(string message)
{
    const int telegramMessageMaxLength = 4096; // https://core.telegram.org/method/messages.sendMessage#return-errors
    const string tooLongMessageSuffix = "...";

    if (string.IsNullOrEmpty(message) || Encoding.UTF8.GetByteCount(message) <= telegramMessageMaxLength)
    {
        return message;
    }

    var encoder = Encoding.UTF8.GetEncoder();
    byte[] buffer = new byte[telegramMessageMaxLength - Encoding.UTF8.GetByteCount(tooLongMessageSuffix)];
    char[] messageChars = message.ToCharArray();
    encoder.Convert(
        chars: messageChars,
        charIndex: 0,
        charCount: messageChars.Length,
        bytes: buffer,
        byteIndex: 0,
        byteCount: buffer.Length,
        flush: false,
        charsUsed: out int charsUsed,
        bytesUsed: out int bytesUsed,
        completed: out bool completed);

    // I don't think we can return message.Substring(0, charsUsed)
    // as that's the number of UTF-16 chars, not the number of codepoints
    // (think about surrogate pairs). Therefore I think we need to
    // actually convert bytes back into a new string
    return Encoding.UTF8.GetString(bytes, 0, bytesUsed) + tooLongMessageSuffix;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...