Разделение текста на куски меньше указанного c размера на новой строке в F # - PullRequest
0 голосов
/ 25 февраля 2020

Допустим, у меня есть какой-то текст:

Lorem ipsum dolor sit amet, consectetur adipiscing elit,\n
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris\n
nisi ut aliquip ex ea commodo consequat.\n
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore\n
eu fugiat nulla pariatur.\n
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia\n
deserunt mollit anim id est laborum.\n

Какой самый эффективный способ разрезать его на куски по x байтов, где разрез может произойти только при возврате каретки?

На ум приходят два метода:

  • разбить текст на строки, добавить строки в буфер, пока буфер не заполнится, откатить последнюю строку, вызвавшую переполнение, и повторить.

  • найти смещение в тексте по длине буфера и вернуться к предыдущему возврату каретки с правильной обработкой начала и конца текста

Мне не удалось найти решение в Интернете, но я не могу поверить, что эта проблема уже не решалась много раз, и может быть общая реализация этого.


Править :

дополнительная информация о моем случае использования:

Код предназначен для бота Telegram, который используется как средство связи с внутренней системой.

Telegram позволяет использовать до 4 КБ за сообщение и регулирует количество звонивших ls.

Сейчас я собираю все сообщения, помещаю их в параллельную очередь, а затем задачи сбрасывают очередь каждую секунду.

Сообщения могут быть одной строкой, могут быть набором строк и иногда может быть больше 4 КБ.

Я беру все сообщения (некоторые из которых состоят из нескольких строк в одном блоке), собираю их в одну строку, затем разделяю строку с помощью возврата каретки, а затем я могу составить блоки до 4кб. Еще одна проблема, с которой я еще не столкнулся, но это на будущее, заключается в том, что Telegram будет отклонять неполную разметку, поэтому мне также придется в какой-то момент обрезать текст на основе этого.

Ответы [ 2 ]

1 голос
/ 26 февраля 2020

Не очень эффективно, а также работает при допущениях

  • , что вы можете захотеть сохранить разделители новой строки, и
  • , что мы можем предположить, что конец строки эквивалентно одной новой строке;

тогда реализация в соответствии с вашим первым подходом является функциональной и простой. Просто разбейте на линии и объедините их, если их общая длина не превышает пороговое значение.

// Comma-separated output of the string lengths
// (plus 1 to compensate for the absence of the EOL)
let printLengths =
    Array.map (String.length >> (+) 1 >> string)
    >> String.concat ", "
    >> printfn "%s"
let text = 
    "Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore
eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia
deserunt mollit anim id est laborum.

"
text.Split '\n' |> printLengths
// prints 57, 67, 67, 41, 77, 26, 74, 37, 1, 1

let foo n (text : string) =
    (text.Split '\n', [])
    ||> Array.foldBack (fun t -> function
    | x::xs when String.length x + t.Length + 1 < n -> x+"\n"+t::xs
    | xs -> t::xs )
text |> foo 108 |> List.toArray |> printLengths
// prints 57, 67, 108, 77, 100, 39
0 голосов
/ 26 февраля 2020

Наиболее распространенные задачи, связанные с потоками, уже очень эффективно реализованы в BCL. Вероятно, это хорошая идея придерживаться проверенных и проверенных Stream классов.

let lipsum  = 
    """
    Lorem ipsum dolor sit amet, consectetur adipiscing elit,
    sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
    Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
    nisi ut aliquip ex ea commodo consequat.
    Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore
    eu fugiat nulla pariatur.
    Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia
    deserunt mollit anim id est laborum.
    """

use stream = new MemoryStream(Encoding.UTF8.GetBytes(lipsum))
use reader =  new StreamReader(stream)

let readBlock blockSize = 
    let writer = new StringBuilder(capacity = blockSize)
    let rec readNextline () =
        if (not reader.EndOfStream) then do
            let line = reader.ReadLine()
            if writer.Capacity < line.Length + writer.Length then do
                stream.Seek(int64 -line.Length, SeekOrigin.Current) |> ignore                                
            else
                writer.AppendLine(line) |> ignore
                readNextline ()

    readNextline ()
    writer.ToString()

readBlock 300 |> printfn "%s"

Вы можете просто грипп sh очереди, записывая в тот же MemoryStream. И позвоните по номеру readBlock, чтобы получать новые блоки не более указанного вами размера.

...