Как создать строку из массива char без копирования?

У меня есть очень большой массив char, который мне нужно преобразовать в строку, чтобы использовать Regex на нем.
Но он настолько велик, что я получаю OutOfMemoryException, когда передаю это конструктору строки.

Я знаю, что строка неизменна, и поэтому не следует указывать ее базовую коллекцию символов, но мне нужно использовать регулярные выражения, не копируя все это.

Как получить этот массив?

  • Я получаю его из файла с помощью StreamReader. Я знаю начальную позицию и длину содержимого для чтения, методы Read и ReadBlock требуют, чтобы я поставлял буфер char[].

Итак, вот что я хочу знать:

  • Есть ли способ указать строку, лежащую в основе коллекции? (Сохраняет ли он свои символы в массиве?)
  • ... или с помощью Regex непосредственно в массиве char?
  • ... или получить часть файла непосредственно в виде строки?

Ответ 1

Я бы подумал, что лучше всего будет читать несколько char [] фрагментов в отдельные строки, которые перекрываются с определенным измерением. Таким образом, вы сможете выполнять свое регулярное выражение на отдельных кусках, и перекрытие обеспечит вам возможность гарантировать, что "разрыв" в кусках не нарушит шаблон поиска. В стиле psuedo-code:

int chunkSize = 100000;
int overLap = 2000;

for(int i = 0; i < myCharArray.length; i += chunkSize - overlap)
{
    // Grab your array chunk into a partial string
    // By having your iteration slightly smaller than 
    // your chunk size you guarantee not to miss any 
    // character groupings. You just need to make sure
    // your overlap is sufficient to cover the expression
    string chunk = new String(myCharArray.Skip(i).Take(chunkSize).ToArray());
    // run your regex
}

Ответ 2

Одним довольно уродливым вариантом будет использование неуправляемой библиотеки RegEx (например, библиотеки регулярных выражений POSIX) и небезопасного кода. Вы можете получить указатель байта * на массив char и передать его непосредственно в неуправляемую библиотеку, а затем отменить ответ.

fixed (byte * pArray = largeCharArray)
{
   // call unmanaged code with pArray
}

Ответ 3

Если у вас есть символ или шаблон, который вы могли бы найти, который гарантированно не находится в шаблоне, который вы пытаетесь найти, вы можете сканировать массив для этого символа и создавать небольшие строки для обработки по отдельности. Процесс будет примерно таким:

char token = '|';
int start = 0;
int length = 0;
for(int i = 0; i < charArray.Length; i++;)
{
    if(charArray[i] == token)
    {
        string split = new string(charArray,start,length);
        // check the string using the regex

        // reset the length
        length = 0;
    }
    else
    {
        length++;
    }
}

Таким образом вы копируете меньшие сегменты строки, которые будут GCed после каждой попытки по сравнению со всей строкой.

Ответ 4

Если вы используете .NET 4.0 или выше, то вы должны использовать MemoryMappedFile. Этот класс был разработан исключительно для того, чтобы вы могли манипулировать очень большими файлами. Из документации MSDN:

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

После того, как вы получили файл с отображением памяти, зайдите в этот ответ о том, как применить RegEx к файлу с отображением памяти.

Надеюсь, это поможет!