Изменение символа в строке в Lua

Есть ли способ заменить символ в позиции N в строке в Lua.

Вот что я придумал до сих пор:

function replace_char(pos, str, r)
    return str:sub(pos, pos - 1) .. r .. str:sub(pos + 1, str:len())
end

str = replace_char(2, "aaaaaa", "X")
print(str)

Я также не могу использовать gsub, так как это заменит каждый захват, а не только захват в позиции N.


person dotminic    schedule 09.03.2011    source источник


Ответы (3)


Строки в Lua неизменяемы. Это означает, что любое решение, заменяющее текст в строке, должно привести к созданию новой строки с желаемым содержимым. В конкретном случае замены одного символа каким-либо другим содержимым вам потребуется разбить исходную строку на часть префикса и часть постфикса и соединить их вместе вокруг нового содержимого.

Этот вариант вашего кода:

function replace_char(pos, str, r)
    return str:sub(1, pos-1) .. r .. str:sub(pos+1)
end

это самый прямой перевод на простой Lua. Это, вероятно, достаточно быстро для большинства целей. Я исправил ошибку, из-за которой префикс должен быть первым символом pos-1, и воспользовался тем фактом, что если последний аргумент string.sub отсутствует, предполагается, что он равен -1, что эквивалентно концу строки.

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

Возможно, что один из двух альтернативных подходов может быть быстрее. Первое — это решение, предложенное Пауло Эберманном, но с одной небольшой поправкой:

function replace_char2(pos, str, r)
    return ("%s%s%s"):format(str:sub(1,pos-1), r, str:sub(pos+1))
end

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

Но имейте в виду, что у string.format могут быть проблемы с любыми \0 символами в любой строке, которую он передает через формат %s. В частности, поскольку она реализована в терминах стандартной функции C sprintf(), было бы разумно ожидать, что она завершит замещаемую строку при первом появлении \0. (Отмечено пользователем Delusional Logic в комментарии.)

Третий вариант, который приходит на ум, заключается в следующем:

function replace_char3(pos, str, r)
    return table.concat{str:sub(1,pos-1), r, str:sub(pos+1)}
end

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

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

person RBerteig    schedule 10.03.2011
comment
Спасибо за подробное объяснение - person dotminic; 11.03.2011
comment
Это старо. Но я только что исправил небольшую ошибку в коде, который написал. Оказывается, метод replace_char2 не вставляет нулевые (\0) символы. - person Delusional Logic; 23.08.2013
comment
@DelusionalLogic Хороший вопрос. string.format основан на стандартной функции C sprintf() и, вероятно, будет иметь проблемы со встроенными байтами NUL. - person RBerteig; 11.02.2014

Вы должны использовать pos внутри своей функции вместо литералов 1 и 3, но кроме этого это выглядит хорошо. Поскольку строки Lua неизменяемы, вы не можете добиться большего успеха, чем это.

Может быть

 "%s%s%s":format(str:sub(1,pos-1), r, str:sub(pos+1, str:len())

более эффективен, чем оператор .., но я в этом сомневаюсь — если он окажется узким местом, измерьте его (а затем решите реализовать эту функцию замены в C).

person Paŭlo Ebermann    schedule 09.03.2011
comment
Да, оператор .. — самый медленный способ объединения строк, поскольку для каждого .. создается новая строка. К более быстрым методам относятся string.format и table.concat. Это не должно вызвать каких-либо заметных эффектов, если только вы не работаете с очень большими строками или множеством операций конкатенации. Например, у меня был сценарий, использующий более 500 МБ памяти для обработки файла размером менее 1 МБ, используя около 5 .. на строку ввода при сортировке и преобразовании ввода в качестве вывода. Изменение его для хранения строк в таблице и table.concat в конце сделало это настолько быстрым, что я даже не стал измерять. - person Arrowmaster; 09.03.2011
comment
@Arrowmaster: Знаете ли вы, что в a .. b .. c созданы две (вместо одной) новых строки, или вы просто предполагаете это? В принципе, это может быть оптимизировано компилятором/интерпретатором для создания только одной новой строки, как это делается в Java для оператора +. Ваш пример - это другой случай, поскольку вам действительно нужно создавать новые строки с каждым оператором. - person Paŭlo Ebermann; 09.03.2011
comment
@Paŭlo Ebermann, да, я просто скопировал код, забыл удалить литералы. @Arrowmaster @Paŭlo Ebermann Я сравню оператор .. с методом форматирования. Спасибо за понимание. - person dotminic; 10.03.2011
comment
@Paŭlo: эталонная версия Lua практически не содержит оптимизаций компилятора. Я не уверен в других реализациях, таких как LuaJIT. - person Arrowmaster; 10.03.2011
comment
Вам нужны скобки вокруг "%s%s%s" здесь. - person Alexander Gladysh; 15.03.2011
comment
Об оптимизации: насколько я помню, стандартный Lua пытается преобразовать все конкатенации .. в одном выражении в одну инструкцию VM (до определенного момента). Таким образом, a .. b .. c не создает промежуточную строку. (Но a .. (b .. c) должен его создать.) - person Alexander Gladysh; 15.03.2011
comment
И обычно table.concat (и создание таблицы, которое для этого требуется) стоят только в циклах. Если у вас есть одно выражение, выберите ... (И, в любом случае, не стоит пытаться оптимизировать преждевременно, сначала напишите максимально лаконично, потом профилируйте и оптимизируйте) - person Alexander Gladysh; 15.03.2011
comment
В Lua есть специальный код операции CONCAT, который не создает промежуточных строк. Использование скобок также приводит к созданию промежуточных строк. - person sylvanaar; 19.03.2011
comment
Делает или нет? «И то и другое» бросает меня. - person Igorio; 16.11.2012
comment
@RossCharette: я полагаю, что нет. Но вы должны включить @sylvanaar в свое сообщение, чтобы он получил уведомление об этом. - person Paŭlo Ebermann; 16.11.2012

С помощью luajit вы можете использовать библиотеку FFI для приведения строки к списку неподписанных диаграмм:

local ffi = require 'ffi'
txt = 'test'
ptr = ffi.cast('uint8_t*', txt)
ptr[1] = string.byte('o')
person Pierco    schedule 18.09.2018