Версия C # StringBuilder, позволяющая использовать строки длиной более 2 миллиардов символов - PullRequest
0 голосов
/ 13 января 2019

В C # 64-битная Windows + .NET 4.5 (или более поздняя версия) + , включающая gcAllowVeryLargeObjects в файле App.config допускает объекты размером более два гигабайта. Это круто, но, к сожалению, максимальное количество элементов, которое C # допускает в массиве символов, по-прежнему ограничено примерно 2 ^ 31 = 2,15 млрд. Символов . Тестирование подтвердило это.

Чтобы преодолеть это, Microsoft рекомендует в варианте B создавать массивы изначально (их «вариант C» даже не компилируется). Это меня устраивает, так как скорость также вызывает беспокойство. Есть ли какой-нибудь проверенный и надежный небезопасный / native / interop / PInvoke код для .NET, который может заменить и действовать как расширенный StringBuilder, чтобы обойти ограничение в 2 миллиарда элементов?

Небезопасный код / ​​pinvoke предпочтителен, но не нарушает условия сделки. В качестве альтернативы, доступна .NET (безопасная) версия?

В идеале замена StringBuilder начинается с малого (желательно пользовательского), а затем многократно увеличивается в два раза при каждом превышении емкости. Я в основном ищу функциональность append() здесь. Сохранение строки в файл также было бы полезно, хотя я уверен, что смогу запрограммировать этот бит, если будет включена функция substring(). Если код использует pinvoke, то, очевидно, необходимо учитывать некоторую степень управления памятью, чтобы избежать потери памяти.

Я не хочу воссоздавать колесо, если какой-то простой код уже существует, но, с другой стороны, я не хочу загружать и включать DLL только для этой простой функциональности.

Я также использую .NET 3.5 для пользователей, у которых не установлена ​​последняя версия Windows.

Ответы [ 2 ]

0 голосов
/ 17 января 2019

Итак, в итоге я создал свою собственную функцию BigStringBuilder. Это список, в котором каждый элемент списка (или страница) является массивом символов (тип List<char[]>).

Если вы используете 64-битную версию Windows, вы можете легко превысить ограничение в 2 миллиарда символов. Мне удалось протестировать создание гигантской строки размером около 32 гигабайт (сначала нужно было увеличить виртуальную память в ОС, в противном случае на моем ПК с 8 ГБ ОЗУ можно было получить только около 7 ГБ). Я уверен, что он легко обрабатывает более 32 ГБ. Теоретически, он должен обрабатывать около 1 000 000 000 * 1 000 000 000 символов или одного квинтиллиона символов, что должно быть достаточно для любого.

По быстродействию некоторые быстрые тесты показывают, что при добавлении он всего на 33% медленнее, чем StringBuilder. У меня была очень похожая производительность, если я выбрал двумерный массив неровных символов (char[][]) вместо List<char[]>, но со списками работать проще, поэтому я остановился на этом.

Надеюсь, кто-то еще найдет это полезным! Там могут быть ошибки, поэтому используйте с осторожностью. Я довольно хорошо это проверил.

// A simplified version specially for StackOverflow
public class BigStringBuilder
{
    List<char[]> c = new List<char[]>();
    private int pagedepth;
    private long pagesize;
    private long mpagesize;         // https://stackoverflow.com/questions/11040646/faster-modulus-in-c-c
    private int currentPage = 0;
    private int currentPosInPage = 0;

    public BigStringBuilder(int pagedepth = 12) {   // pagesize is 2^pagedepth (since must be a power of 2 for a fast indexer)
        this.pagedepth = pagedepth;
        pagesize = (long)Math.Pow(2, pagedepth);
        mpagesize = pagesize - 1;
        c.Add(new char[pagesize]);
    }

    // Indexer for this class, so you can use convenient square bracket indexing to address char elements within the array!!
    public char this[long n]    {
        get { return c[(int)(n >> pagedepth)][n & mpagesize]; }
        set { c[(int)(n >> pagedepth)][n & mpagesize] = value; }
    }

    public string[] returnPagesForTestingPurposes() {
        string[] s = new string[currentPage + 1];
        for (int i = 0; i < currentPage + 1; i++) s[i] = new string(c[i]);
        return s;
    }
    public void clear() {
        c = new List<char[]>();
        c.Add(new char[pagesize]);
        currentPage = 0;
        currentPosInPage = 0;
    }


    public void fileOpen(string path)
    {
        clear();
        StreamReader sw = new StreamReader(path);
        int len = 0;
        while ((len = sw.ReadBlock(c[currentPage], 0, (int)pagesize)) != 0) {
            if (!sw.EndOfStream)    {
                currentPage++;
                if (currentPage > (c.Count - 1)) c.Add(new char[pagesize]);
            }
            else    {
                currentPosInPage = len;
                break;
            }
        }
        sw.Close();
    }

    // See: https://stackoverflow.com/questions/373365/how-do-i-write-out-a-text-file-in-c-sharp-with-a-code-page-other-than-utf-8/373372
    public void fileSave(string path)   {
        StreamWriter sw = File.CreateText(path);
        for (int i = 0; i < currentPage; i++) sw.Write(new string(c[i]));
        sw.Write(new string(c[currentPage], 0, currentPosInPage));
        sw.Close();
    }

    public long length()    {
        return (long)currentPage * (long)pagesize + (long)currentPosInPage;
    }

    public string ToString(long max = 2000000000)   {
        if (length() < max) return substring(0, length());
        else return substring(0, max);
    }

    public string substring(long x, long y) {
        StringBuilder sb = new StringBuilder();
        for (long n = x; n < y; n++) sb.Append(c[(int)(n >> pagedepth)][n & mpagesize]);    //8s
        return sb.ToString();
    }

    public bool match(string find, long start = 0)  {
        //if (s.Length > length()) return false;
        for (int i = 0; i < find.Length; i++) if (i + start == find.Length || this[start + i] != find[i]) return false;
        return true;
    }
    public void replace(string s, long pos) {
        for (int i = 0; i < s.Length; i++)  {
            c[(int)(pos >> pagedepth)][pos & mpagesize] = s[i];
            pos++;
        }
    }

    public void Append(string s)
    {
        for (int i = 0; i < s.Length; i++)
        {
            c[currentPage][currentPosInPage] = s[i];
            currentPosInPage++;
            if (currentPosInPage == pagesize)
            {
                currentPosInPage = 0;
                currentPage++;
                if (currentPage == c.Count) c.Add(new char[pagesize]);
            }
        }
    }


}
0 голосов
/ 13 января 2019

Размер строк в C ++ не ограничен согласно этому ответу .

Вы можете написать свой код обработки строк на C ++ и использовать импорт DLL для связи между вашим кодом C # и кодом C ++. Это упрощает вызов ваших функций C ++ из кода C #.

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

Здесь - это ссылка на страницу проекта кода, которая дает некоторые рекомендации по импорту DLL из C # в C ++.

...