Как создать строку из массива 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 к файлу с отображением памяти.
Надеюсь, это поможет!