как мне обрезать строку, не копируя ее

#ruby

#ruby

Вопрос:

Я хотел бы вырезать несколько символов из начала очень большой строки. Чтобы сэкономить мне немного памяти и времени, я хотел бы избежать создания ее копии, а просто позволить строке начинаться с этого более позднего индекса, как это было бы легко возможно в C, просто увеличив указатель на строку (массив).

Возможно ли это?

Комментарии:

1. Какие операции вы хотите выполнить над строкой после ее обрезки?

2. Я анализирую CSV с помощью FasterCSV, перехватываю MalformedCSVErrors и повторно обрабатываю CSV из строки после неправильно сформированной. Поэтому мне нужно вырезать начало CSV. Я знаю, что я мог бы также выполнить потоковый синтаксический анализ CSV из исходного файла, но наша архитектура этого не позволяет (пока).

3. Можете ли вы привести пример?

4. Конечно: gist.github.com/952883

Ответ №1:

Вы не можете использовать трюк «увеличить указатель на строку», но попробуйте это:

 str = 'I would like to cut a number of characters from the beginning of a very large String'
str[0, 10] = ''
str # => "ke to cut a number of characters from the beginning of a very large String"
  

Это самый простой из известных мне способов обрезать часть строки в Ruby.

Ответ №2:

Вероятно, невозможно сделать то, что вы хотели бы, из-за способа, которым Ruby представляет строки.

Как вы упомянули, в C вы могли бы просто увеличить указатель на строку на некоторое количество символов. Однако в Ruby класс String реализован с помощью, struct RString который имеет ptr поле для базовой строки C. Для Ruby не имело бы смысла предоставлять метод, который увеличивает поле ptr, потому что это привело бы к потере отслеживания исходного местоположения и усложнило бы освобождение этой памяти. Возможно, вы сможете написать свое собственное расширение для класса String, которое делает то, что вы хотите, но, вероятно, возникнут дополнительные сложности (например, в отношении хеширования).

Комментарии:

1. На самом деле, это возможно и lstrip! реализовано таким образом . (Не будучи специалистом по C, не могу сказать, как выполняется освобождение)

Ответ №3:

Если с памятью проблем нет и вам нужно избавиться от операции копирования, почему бы не создать свой собственный класс String, реализованный как array:

 class StringAsArray
    def initialize(s)
        @arr = s.split(//)
    end

    // .... your methods goes here ....

end
  

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

Комментарии:

1. «Если память не является проблемой» разве мы не пытались сэкономить память в первую очередь? (И не похоже, что это решение экономит время)

Ответ №4:

Вы можете использовать gsub! , если знаете, какую часть нужно удалить.

 s = 'cut this string'
s.gsub!('cut ', '')
s
# => "cut this string"
  

или вы можете использовать slice!

 s.slice!(s.index('this')..-1)
s
# => "cut this string"
  

Комментарии:

1. Вы уверены, что это позволяет избежать копирования строки?

2. bang! Версия изменяет строку на месте, не создавая копию.

3. @Nikita Rybak Я ответил на вопрос. Если вы используете bang! версия, строка изменяется на месте без выделения нового экземпляра.

4. Object_id остается тем же самым. Но не означает ли это, что содержимое копируется в начало выделенной памяти? Или object_id не представляет адрес в памяти?

Ответ №5:

Не совсем уверен, что строка #reverse! не копирует внутренне, но это метод, который я использовал в прошлом:

 mystring.reverse!
num_of_chars.times {mystring.chop!}
mystring.reverse!
  

Хотя теперь, когда я смотрю на это, я думаю, что у Железного дровосека есть более элегантное решение.