Разделите большой набор символов на части с помощью указанного набора разделителей и максимального размера фрагмента

Проблема

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

Чтобы поместить это в псевдокод, мне в основном нужны:

foreach(var chunk in text.Chunk(x).ButAlsoSplitOnFirst('.', '!', '?', etc...);

Мое решение

public static class StringExtensions
    {
        public static IEnumerable<string> ChunkWithDelimeters(this string source, int maxChar, params char[] delimeters)
        {
            var sourceSpan = source.AsSpan();
            var items = new List<string>();

            var pointer = 0;
            var lazyPointer = 0;

            while (pointer <= sourceSpan.Length)
            {
                bool foundMatch = false;

                pointer = Math.Min(lazyPointer + maxChar, sourceSpan.Length);

                if (pointer == sourceSpan.Length)
                {
                    items.Add(sourceSpan.Slice(lazyPointer, pointer - lazyPointer).TrimStart().ToString());
                    break;
                }

                for (var j = pointer; j >= lazyPointer; j--)
                {
                    var tempChar = sourceSpan[j];

                    if (delimeters.Contains(tempChar))
                    {
                        foundMatch = true;
                        items.Add(sourceSpan.Slice(lazyPointer, j + 1 - lazyPointer).TrimStart().ToString());
                        lazyPointer = j + 1;
                    }
                }

                if (!foundMatch)
                {
                    items.Add(sourceSpan.Slice(lazyPointer, pointer - lazyPointer).Trim().ToString());
                    lazyPointer = pointer;
                }
            }

            return items;
        }
    }

Контрольные точки

source.LengthmaxCharразделителиСреднее (мкс)Ошибка (мкс)StdDev (мкс)
732000030‘.’73 622,7391 412 40351,252,0589
43920030‘.’4,122,57468,639260,8468
73130‘.’5,1250,04660,0436

Мой вопрос

Какое оптимальное решение этой проблемы? Есть ли способ улучшить производительность моего решения?

1 ответ
1

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

if (pointer == sourceSpan.Length)
{
    items.Add(sourceSpan.Slice(lazyPointer, pointer - lazyPointer).TrimStart().ToString());
    break;
}

Также вы можете использовать IndexOfAny вместо цикла в конце

ПРЕДУПРЕЖДЕНИЕ этот код не тестировался и просто использовал пример

var slice = sourceSpan.Slice(lazyPointer, pointer - lazyPointer);
var contains = slice.IndexOfAny(delimeters);
while (contains > -1)
{
    items.Add(slice.Slice(0, contains + 1).ToString());
    slice = slice.Slice(contains + 1);
    contains = slice.IndexOfAny(delimeters);
}

Теперь еще несколько моментов, которые, я думаю, вам следует сначала разделить по разделителям, а затем по кускам. Вместо кусков — разделители. Если у меня есть строка «12345.6789.8.654321», разделенная на 7. Первые два выходящих будут «12345»

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *