Lua 문자열에서 개별 문자를 반복하는 방법은 무엇입니까?


88

Lua에 문자열이 있고 그 안의 개별 문자를 반복하고 싶습니다. 그러나 내가 시도한 코드는 없으며 공식 매뉴얼은 하위 문자열을 찾고 바꾸는 방법 만 보여줍니다.

str = "abcd"
for char in str do -- error
  print( char )
end

for i = 1, str:len() do
  print( str[ i ] ) -- nil
end

답변:


124

lua 5.1에서는 몇 가지 방법으로 문자열의 문자를 반복 할 수 있습니다.

기본 루프는 다음과 같습니다.

i = 1 인 경우 #str do
    로컬 c = str : sub (i, i)
    -c로 무언가를하십시오
종료

그러나 패턴을 사용 string.gmatch()하여 문자에 대한 반복자를 얻는 것이 더 효율적일 수 있습니다 .

for c in str : gmatch "." 하다
    -c로 무언가를하십시오
종료

또는 string.gsub()각 문자에 대한 함수를 호출하는 데 사용할 수도 있습니다 .

str : gsub ( ".", function (c)
    -c로 무언가를하십시오
종료)

위의 모든 과정에서 string모듈이 모든 문자열 값에 대한 메타 테이블로 설정되어 있으므로 해당 함수를 :표기법을 사용하여 멤버로 호출 할 수 있다는 사실을 활용 했습니다 . 또한 #문자열 길이를 얻기 위해 (5.1, IIRC의 새로운 기능)을 사용했습니다 .

애플리케이션에 대한 최선의 답은 많은 요인에 따라 달라지며 성능이 중요하다면 벤치 마크가 친구가됩니다.

문자를 반복해야하는 이유 를 평가 하고 Lua에 바인딩 된 정규식 모듈 중 하나를 살펴 보거나, 현대적인 접근 방식을 위해 Lua에 대한 구문 분석 표현식 그래머를 구현하는 Roberto의 lpeg 모듈을 살펴볼 수 있습니다.


감사. 언급 한 lpeg 모듈에 대해-토큰 화 후 원본 텍스트에 토큰 위치를 저장합니까? 내가 수행해야 할 작업은 lua를 통해 scite에서 특정 간단한 언어를 구문 강조 표시하는 것입니다 (컴파일 된 C ++ 파서 없음). 또한 lpeg를 설치하는 방법은 무엇입니까? 배포판에 .c 소스가있는 것 같습니다. lua와 함께 컴파일해야합니까?
grigoryvp

lpeg를 빌드하면 require에서 찾을 수있는 곳에 저장해야하는 DLL (또는 .so)이 생성됩니다. (즉, lua 설치에서 전역 package.cpath의 내용으로 식별되는 어딘가에 있습니다.) 단순화 된 구문을 사용하려면 동반 모듈 re.lua도 설치해야합니다. lpeg 문법에서 여러 가지 방법으로 콜백을 받고 텍스트를 캡처 할 수 있으며 캡처를 사용하여 나중에 사용하기 위해 일치 위치를 간단히 저장할 수 있습니다. 구문 강조가 목표라면 PEG는 잘못된 도구 선택이 아닙니다.
RBerteig

3
SciTE최신 릴리스 (2.22 이후)에는 LPEG 기반 렉서 인 Scintillua가 포함되어 있습니다. 즉, 재 컴파일없이 바로 사용할 수 있습니다.
Stuart P. Bentley

11

Lua 5를 사용하는 경우 다음을 시도하십시오.

for i = 1, string.len(str) do
    print( string.sub(str, i, i) )
end

9

당면한 작업에 따라 사용하기가 더 쉬울 수 있습니다 string.byte. 또한 각 새 문자열을 해싱하고 이미 알고 있는지 확인하기 때문에 Lua에서 비용이 많이 드는 새 하위 문자열을 만들지 않기 때문에 가장 빠른 방법입니다. string.byte가독성과 이식성을 유지하기 위해 찾고있는 기호의 코드를 미리 계산할 수 있습니다 .

local str = "ab/cd/ef"
local target = string.byte("/")
for idx = 1, #str do
   if str:byte(idx) == target then
      print("Target found at:", idx)
   end
end

5

제공된 답변 ( here , herehere ) 에는 이미 많은 좋은 접근 방식이 있습니다 . 속도가 당신이 주로 찾고 있는 것이라면 루아의 C API를 통해 작업을하는 것을 확실히 고려해야합니다. 이것은 원시 Lua 코드보다 몇 배나 빠릅니다. 미리로드 된 청크 (예 : load function )로 작업 할 때 차이는 그다지 크지는 않지만 여전히 상당합니다.

순수한 Lua 솔루션에 대해 제가 만든이 작은 벤치 마크를 공유하겠습니다. 이 날짜에 제공된 모든 답변을 다루고 몇 가지 최적화를 추가합니다. 그래도 고려해야 할 기본 사항은 다음과 같습니다.

문자열의 문자를 몇 번 반복해야합니까?

  • 대답이 "한 번"이면 banchmark의 첫 부분 ( "raw speed")을 찾아야합니다.
  • 그렇지 않으면 두 번째 부분은 문자열을 테이블로 구문 분석하여 반복하는 속도가 훨씬 빠르기 때문에 더 정확한 추정을 제공합니다. @Jarriz가 제안한 것과 같은 간단한 함수 작성도 고려해야합니다.

다음은 전체 코드입니다.

-- Setup locals
local str = "Hello World!"
local attempts = 5000000
local reuses = 10 -- For the second part of benchmark: Table values are reused 10 times. Change this according to your needs.
local x, c, elapsed, tbl
-- "Localize" funcs to minimize lookup overhead
local stringbyte, stringchar, stringsub, stringgsub, stringgmatch = string.byte, string.char, string.sub, string.gsub, string.gmatch

print("-----------------------")
print("Raw speed:")
print("-----------------------")

-- Version 1 - string.sub in loop
x = os.clock()
for j = 1, attempts do
    for i = 1, #str do
        c = stringsub(str, i)
    end
end
elapsed = os.clock() - x
print(string.format("V1: elapsed time: %.3f", elapsed))

-- Version 2 - string.gmatch loop
x = os.clock()
for j = 1, attempts do
    for c in stringgmatch(str, ".") do end
end
elapsed = os.clock() - x
print(string.format("V2: elapsed time: %.3f", elapsed))

-- Version 3 - string.gsub callback
x = os.clock()
for j = 1, attempts do
    stringgsub(str, ".", function(c) end)
end
elapsed = os.clock() - x
print(string.format("V3: elapsed time: %.3f", elapsed))

-- For version 4
local str2table = function(str)
    local ret = {}
    for i = 1, #str do
        ret[i] = stringsub(str, i) -- Note: This is a lot faster than using table.insert
    end
    return ret
end

-- Version 4 - function str2table
x = os.clock()
for j = 1, attempts do
    tbl = str2table(str)
    for i = 1, #tbl do -- Note: This type of loop is a lot faster than "pairs" loop.
        c = tbl[i]
    end
end
elapsed = os.clock() - x
print(string.format("V4: elapsed time: %.3f", elapsed))

-- Version 5 - string.byte
x = os.clock()
for j = 1, attempts do
    tbl = {stringbyte(str, 1, #str)} -- Note: This is about 15% faster than calling string.byte for every character.
    for i = 1, #tbl do
        c = tbl[i] -- Note: produces char codes instead of chars.
    end
end
elapsed = os.clock() - x
print(string.format("V5: elapsed time: %.3f", elapsed))

-- Version 5b - string.byte + conversion back to chars
x = os.clock()
for j = 1, attempts do
    tbl = {stringbyte(str, 1, #str)} -- Note: This is about 15% faster than calling string.byte for every character.
    for i = 1, #tbl do
        c = stringchar(tbl[i])
    end
end
elapsed = os.clock() - x
print(string.format("V5b: elapsed time: %.3f", elapsed))

print("-----------------------")
print("Creating cache table ("..reuses.." reuses):")
print("-----------------------")

-- Version 1 - string.sub in loop
x = os.clock()
for k = 1, attempts do
    tbl = {}
    for i = 1, #str do
        tbl[i] = stringsub(str, i) -- Note: This is a lot faster than using table.insert
    end
    for j = 1, reuses do
        for i = 1, #tbl do
            c = tbl[i]
        end
    end
end
elapsed = os.clock() - x
print(string.format("V1: elapsed time: %.3f", elapsed))

-- Version 2 - string.gmatch loop
x = os.clock()
for k = 1, attempts do
    tbl = {}
    local tblc = 1 -- Note: This is faster than table.insert
    for c in stringgmatch(str, ".") do
        tbl[tblc] = c
        tblc = tblc + 1
    end
    for j = 1, reuses do
        for i = 1, #tbl do
            c = tbl[i]
        end
    end
end
elapsed = os.clock() - x
print(string.format("V2: elapsed time: %.3f", elapsed))

-- Version 3 - string.gsub callback
x = os.clock()
for k = 1, attempts do
    tbl = {}
    local tblc = 1 -- Note: This is faster than table.insert
    stringgsub(str, ".", function(c)
        tbl[tblc] = c
        tblc = tblc + 1
    end)
    for j = 1, reuses do
        for i = 1, #tbl do
            c = tbl[i]
        end
    end
end
elapsed = os.clock() - x
print(string.format("V3: elapsed time: %.3f", elapsed))

-- Version 4 - str2table func before loop
x = os.clock()
for k = 1, attempts do
    tbl = str2table(str)
    for j = 1, reuses do
        for i = 1, #tbl do -- Note: This type of loop is a lot faster than "pairs" loop.
            c = tbl[i]
        end
    end
end
elapsed = os.clock() - x
print(string.format("V4: elapsed time: %.3f", elapsed))

-- Version 5 - string.byte to create table
x = os.clock()
for k = 1, attempts do
    tbl = {stringbyte(str,1,#str)}
    for j = 1, reuses do
        for i = 1, #tbl do
            c = tbl[i]
        end
    end
end
elapsed = os.clock() - x
print(string.format("V5: elapsed time: %.3f", elapsed))

-- Version 5b - string.byte to create table + string.char loop to convert bytes to chars
x = os.clock()
for k = 1, attempts do
    tbl = {stringbyte(str, 1, #str)}
    for i = 1, #tbl do
        tbl[i] = stringchar(tbl[i])
    end
    for j = 1, reuses do
        for i = 1, #tbl do
            c = tbl[i]
        end
    end
end
elapsed = os.clock() - x
print(string.format("V5b: elapsed time: %.3f", elapsed))

출력 예 (Lua 5.3.4, Windows) :

-----------------------
Raw speed:
-----------------------
V1: elapsed time: 3.713
V2: elapsed time: 5.089
V3: elapsed time: 5.222
V4: elapsed time: 4.066
V5: elapsed time: 2.627
V5b: elapsed time: 3.627
-----------------------
Creating cache table (10 reuses):
-----------------------
V1: elapsed time: 20.381
V2: elapsed time: 23.913
V3: elapsed time: 25.221
V4: elapsed time: 20.551
V5: elapsed time: 13.473
V5b: elapsed time: 18.046

결과:

내 경우, string.byte그리고 string.sub원시 속도면에서 빠른했다. 캐시 테이블을 사용하고 루프 당 10 번 재사용하면 string.byte문자 코드를 문자로 다시 변환 할 때도 버전이 가장 빠릅니다 (항상 필요한 것은 아니며 사용량에 따라 다름).

아시다시피 이전 벤치 마크를 기반으로 몇 가지 가정을하고이를 코드에 적용했습니다.

  1. 라이브러리 함수는 훨씬 더 빠르기 때문에 루프 내에서 사용되는 경우 항상 지역화되어야합니다.
  2. lua 테이블에 새 요소를 삽입하는 것은를 사용하는 tbl[idx] = value것보다 훨씬 빠릅니다 table.insert(tbl, value).
  3. 를 사용하여 테이블을 반복 for i = 1, #tbl하는 것이 for k, v in pairs(tbl).
  4. 호출 자체가 실행 시간을 약간 추가하기 때문에 항상 함수 호출이 적은 버전을 선호합니다.

도움이 되었기를 바랍니다.


0

모든 사람들은 덜 최적의 방법을 제안합니다

최선일 것 :

    function chars(str)
        strc = {}
        for i = 1, #str do
            table.insert(strc, string.sub(str, i, i))
        end
        return strc
    end

    str = "Hello world!"
    char = chars(str)
    print("Char 2: "..char[2]) -- prints the char 'e'
    print("-------------------\n")
    for i = 1, #str do -- testing printing all the chars
        if (char[i] == " ") then
            print("Char "..i..": [[space]]")
        else
            print("Char "..i..": "..char[i])
        end
    end

어떤 작업에 "최적화"되지 않았습니까? 어떤 작업에 "최고"입니까?
Oleg V. Volkov

0

문자열을 생성하기 위해 반복하고 load ()를 사용하여이 문자열을 테이블로 반환합니다.

itab=function(char)
local result
for i=1,#char do
 if i==1 then
  result=string.format('%s','{')
 end
result=result..string.format('\'%s\'',char:sub(i,i))
 if i~=#char then
  result=result..string.format('%s',',')
 end
 if i==#char then
  result=result..string.format('%s','}')
 end
end
 return load('return '..result)()
end

dump=function(dump)
for key,value in pairs(dump) do
 io.write(string.format("%s=%s=%s\n",key,type(value),value))
end
end

res=itab('KOYAANISQATSI')

dump(res)

내 놓는다 ...

1=string=K
2=string=O
3=string=Y
4=string=A
5=string=A
6=string=N
7=string=I
8=string=S
9=string=Q
10=string=A
11=string=T
12=string=S
13=string=I
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.