2009-06-16 2 views
3

Возможно ли это? Учитывая, что C# использует неизменяемые строки, можно было бы ожидать, что будет метод вдоль линий:Обмен символьным буфером между объектами строк C#

var expensive = ReadHugeStringFromAFile(); 
var cheap = expensive.SharedSubstring(1); 

Если нет такой функции, то зачем с созданием строки неизменны? Или, альтернативно, если строки уже неизменяемы по другим причинам, почему бы не предоставить этот метод?

Конкретная причина, по которой я занимаюсь этим, заключается в синтаксическом анализе файлов. Простые рекурсивные анализаторы спуска (например, созданные TinyPG или легко написанные вручную) используют подстроку по всему месту. Это означает, что если вы даете им большой файл для разбора, отключение памяти невероятно. Конечно, есть обходные пути - в основном сворачивайте свой собственный класс SubString, а затем, конечно, забывайте о возможности использования таких методов String, как StartsWith или String, таких как Regex, поэтому вам нужно также перевернуть свою собственную версию. Я предполагаю, что генераторы парсеров, такие как ANTLR, в основном делают это, но мой формат достаточно прост, чтобы не оправдывать использование такого монстра. Даже TinyPG, вероятно, перебор.

Кто-то пожалуйста, скажите мне, что я не хватает какой-то очевидный или не столь очевидный стандарт C# вызов метода где-то ...

+0

Я только что испытал потенциальное обходное решение. Одним из способов решения проблемы было бы совпадение регулярного выражения с серединой строки. Это возможно, если вы присоедините «^. {N}» к началу Regex. Однако, похоже, библиотека Regex недостаточно умна, чтобы просто пропускать N символов за одну операцию. Это занимает время O (N), поэтому матчи начинают больше и дольше по мере роста N. Вздох. –

+0

Hah, конечно, Regex.Match имеет необязательный параметр startat, который, очевидно, O (1). Поэтому обходной путь действительно работает. Я бы не назвал это чистым решением, но это будет ... –

ответ

5

Нет, ничего подобного.

Строки .NET содержат свои текстовые данные напрямую, в отличие от строк Java, которые имеют ссылку на массив символов, смещение и длину.

Оба решения имеют «выигрыши» в некоторых ситуациях и потери в других.

Если вы абсолютно уверены, что это будет убийцей для вас, вы можете реализовать строку стиля Java для использования в своих внутренних API.

+0

Вот чего я боялся. Вздох. Спасибо ... –

+0

А, мне было просто интересно об этом сегодня. –

1

.NET framework поддерживает string interning. Это частичное решение, но не дает возможности повторно использовать части строки. Я думаю, что повторное использование подстроки вызовет некоторые проблемы, а не obviouse при первом взгляде. Если вам нужно много манипулировать строкой, используя StringBuilder, это путь.

+0

Штрих-интернирование (как в Java, так и в C#) является мертвым мозгом, поскольку требует, чтобы вы построили строковый объект, прежде чем вы вызовете Intern. Для чего-то с заявленной целью экономии использования памяти вы ожидаете, что сможете передать ему массив символов StringBuilder или (срез a) или что-то в этом роде. Но нет, «это было бы слишком легко». –

+0

Это правда ... но GC ...: D –

+0

Поскольку я не могу применить регулярное выражение к (префиксу) StringBuilder, не создавая строку (и, следовательно, копируя символы), вы все равно сталкиваетесь с проблемой копирование 5G текста при анализе 1M-файла. Я мог бы просто сказать, что ни один из моих токенов длиннее X символов и многократно генерирует префиксные строки из StringBuilder, которые могут просто работать ... простых это не будет. Вздох. –

2

Насколько я знаю, все сильные парсеры используют потоки для анализа. Разве это не подходит для вашей ситуации?

+0

Нет, потому что я не могу применить регулярное выражение к потоку или даже выполнить простой неразрушающий тест, начинается ли он с литеральной строки. Поэтому прямой анализ потока требует повторной реализации всей сложности Regex (по крайней мере), что и делает ANTLR, поэтому я назвал его «монстром» по сравнению с чем-то тривиальным, как TinyPG. Тем не менее, TinyPG повторно вызывает Substring, чтобы получить остальную часть ввода. Дайте ему 1 МБ-файл, который разделен на 100 000 токенов, и вы в конечном итоге копируете 5 ГБ памяти. Хлоп! –

0

Ничто в C# не предоставляет вам готовые функции, которые вы ищете.

Что нужно - это Rope data structure, непреложная структура данных, которая поддерживает O (1) concats и O (log n) подстроки. Я не могу найти каких-либо реализаций C# для веревки, но here a Java one.

Запрет на то, что нет ничего плохого в использовании TinyPG или ANTLR, если это самый простой способ добиться результата.

0

Ну, вы можете использовать «небезопасные» для самостоятельного управления памятью, что может позволить вам делать то, что вы ищете. Также класс StringBuilder отлично подходит для ситуаций, когда нужно многократно обрабатывать строку, поскольку она не создает новую строку при каждой манипуляции.

+0

Я хотел бы сделать это, за исключением того, что вы не можете применить Regex к StringBuffer :-( –

0

Вы можете легко написать тривиальный класс для представления «дешевых». Он просто удерживал бы индекс начала подстроки и длину подстроки.Несколько методов позволят вам читать подстроки, когда это необходимо - оператор струны литого бы идеально, как вы могли бы использовать

string text = myCheapObject; 

и он будет работать плавно, как если бы это была фактическая строка. Добавление поддержки нескольких удобных методов, таких как StartsWith, было бы быстрым и легким (все они были бы одним лайнером).

Другой вариант - написать регулярный синтаксический анализатор и сохранить токены в словаре, из которого вы делитесь ссылками на токены, а не на нескольких копиях.

+0

Если вы создадите класс CheapString, это не сработает с regex – albertein

+0

Да, проблема в том, что строка запечатана, поэтому вы можете Из этого следует, что когда вы загружаете и разбираете 500-мегабайтный файл, вы получаете 1 ГБ (UTF8 до wchar), который идет на 2 ГБ после того, как вы начнете рубить его. В моем случае элементы появляются из порядок, и файл может быть в медленной сети, поэтому даже если вы можете реорганизовать его поток, производительность будет ужасной. – russbishop

+0

@xenadu, вы можете написать потоковое представление и по-прежнему использовать большие буферы ввода-вывода, чтобы обеспечить хорошую производительность. есть набор токенов const в файле, тогда вы не должны нарезать их на тысячи крошечных строк, а всего лишь тысячи ссылок (или индексов) на строки в небольшом словаре. Аналогично, если у вас есть число, представленное в виде строки, например "123765.6567853567896" затем преобразование его в float позволит сэкономить 36 байт или более. Обычно есть много вещей, которые вы можете сделать, чтобы повысить скорость и эффективность памяти. если это не удается, получите 64-битный компьютер и Chuck 256 ГБ оперативной памяти или даже SSD. –

Смежные вопросы