루아 레퍼런스 매뉴얼 — 표준 라이브러리: 문자열·UTF-8
루아 레퍼런스 매뉴얼 — 표준 라이브러리: 문자열·UTF-8
루아의 표준 라이브러리 중 **문자열 조작(string manipulation)**과 **UTF-8 지원(UTF-8 support)**을 다루는 섹션 6.4와 6.5를 옮겨왔어요. 문자열은 어느 언어든 가장 흔하게 만지는 데이터 타입이라, 이 라이브러리는 그만큼 실용적이에요. 부분 문자열을 찾고 추출하는 함수부터 그 유명한 패턴 매칭(pattern matching), 그리고 이진 데이터를 다룰 때 쓰는 pack/unpack 포맷 문자열까지 한 번에 챙겨볼게요. UTF-8 쪽은 이름 그대로 UTF-8 인코딩을 다루는 데 필요한 기본 함수들을 담고 있어요.
번역할 때 함수 시그니처, 패턴 기호, pack/unpack 포맷 문자열, 그리고 모든 코드는 원문 그대로 보존했어요. 설명은 강사가 옆에서 말해 주는 톤으로 다듬었죠.
출처: Lua 5.4 Reference Manual — §6.4 String Manipulation, §6.5 UTF-8 Support (https://www.lua.org/manual/5.4/manual.html)
본문
6.4 – 문자열 조작 (String Manipulation)
이 라이브러리는 문자열 조작을 위한 범용 함수들을 제공해요. 부분 문자열을 찾고 추출하는 함수, 그리고 패턴 매칭이 대표적이죠.
루아에서 문자열을 인덱싱할 때, 첫 번째 문자는 위치 1에 있어요. C처럼 0부터 시작하는 게 아니에요. 그리고 인덱스는 음수가 될 수 있는데, 그러면 문자열의 끝에서부터 거꾸로 샌다고 해석해요. 그래서 마지막 문자가 위치 -1, 그 앞이 -2, 이런 식으로요.
string 라이브러리는 모든 함수를 string 테이블 안에 담아둬요. 거기에 더해, 문자열에 대한 메타테이블을 만들어서 __index 필드가 string 테이블을 가리키게 해두죠. 덕분에 문자열 함수를 객체 지향 스타일로 쓸 수 있어요. 예를 들어 string.byte(s,i)는 s:byte(i)처럼 쓸 수 있어요.
주의할 점 하나. string 라이브러리는 1바이트 문자 인코딩을 가정해요.
string.byte (s [, i [, j]])
s[i], s[i+1], ···, s[j] 문자들의 내부 수치 코드를 반환해요.
i의 기본값은 1이고, j의 기본값은 i예요. 이 인덱스들은 string.sub 함수와 같은 규칙에 따라 보정돼요.
수치 코드는 플랫폼 간에 반드시 이식 가능하지는 않아요.
string.char (···)
0개 이상의 정수를 받아서, 각 인자에 대응하는 내부 수치 코드를 가진 문자 하나씩으로 이루어진 문자열을 반환해요. 반환되는 문자열의 길이는 인자의 개수와 같죠.
역시, 수치 코드는 플랫폼 간에 반드시 이식 가능하지는 않아요.
string.dump (function [, strip])
주어진 함수의 이진 표현(binary chunk)을 담은 문자열을 반환해요. 나중에 이 문자열을 load 하면 함수의 복사본(하지만 새로운 upvalue들을 가진)을 얻을 수 있어요.
strip이 참 값이면, 공간을 아끼기 위해 이진 표현이 함수에 대한 모든 디버그 정보를 포함하지 않을 수 있어요.
upvalue를 가진 함수는 upvalue의 개수만 저장해요. 다시 (로)드되면 그 upvalue들은 새 인스턴스를 받게 되죠. (이 upvalue들이 어떻게 초기화되는지는 load 함수에서 자세히 설명해요. 필요에 맞게 함수의 upvalue를 직렬화해서 다시 로드하려면 debug 라이브러리를 쓰면 돼요.)
string.find (s, pattern [, init [, plain]])
문자열 s에서 pattern(§6.4.1 참조)의 첫 번째 일치를 찾아요. 일치하는 걸 찾으면 find는 그 발생이 시작되고 끝나는 s의 인덱스들을 반환하고, 그렇지 않으면 fail을 반환해요.
세 번째 옵션인 숫자 인자 init은 탐색을 시작할 위치를 지정해요. 기본값은 1이고 음수도 될 수 있어요. 네 번째 옵션인 plain이 true면 패턴 매칭 기능이 꺼져서, 패턴의 어떤 문자도 마법(magic)으로 취급하지 않는 평범한 "부분 문자열 찾기" 연산을 해요.
패턴에 캡처(capture)가 있다면, 성공적인 일치에서 그 캡처된 값들도 두 인덱스 뒤에 이어서 반환돼요.
string.format (formatstring, ···)
첫 번째 인자로 주어진 문자열 설명을 따라, 가변 개수의 인자들을 포맷한 버전을 반환해요.
포맷 문자열은 ISO C 함수 sprintf와 같은 규칙을 따라요. 차이는 두 가지예요. 변환 지정자와 수정자 F, n, *, h, L, l이 지원되지 않고, 대신 추가 지정자 q가 있다는 점이죠. 그리고 너비(width)와 정밀도(precision)는 있을 때 두 자리 숫자로 제한돼요.
지정자 q는 불리언, nil, 숫자, 문자열을 루아 소스 코드에서 유효한 상수가 되는 방식으로 포맷해요. 불리언과 nil은 당연히 true, false, nil로 쓰여요. 부동소수점은 전체 정밀도를 보존하려고 16진수로 쓰여요. 문자열은 큰따옴표 사이에 쓰는데, 루아 인터프리터가 안전하게 다시 읽어낼 수 있도록 필요할 때 이스케이프 시퀀스를 사용해요. 예를 들어 다음 호출은
string.format('%q', 'a string with "quotes" and \n new line')
이런 문자열을 만들어낼 수 있어요:
"a string with \"quotes\" and \
new line"
이 지정자는 수정자(플래그, 너비, 정밀도)를 지원하지 않아요.
변환 지정자 A, a, E, e, f, G, g는 모두 숫자를 인자로 기대해요. c, d, i, o, u, X, x라는 지정자들은 정수를 기대하죠. 루아가 C89 컴파일러로 컴파일될 때는 A와 a(16진수 부동소수점) 지정자가 수정자를 지원하지 않아요.
지정자 s는 문자열을 기대해요. 인자가 문자열이 아니면 tostring과 같은 규칙으로 문자열로 변환돼요. 지정자에 수정자가 있으면 그에 해당하는 문자열 인자는 내장된 0을 포함하면 안 돼요.
지정자 p는 lua_topointer가 반환하는 포인터를 포맷해요. 이건 테이블, userdata, 스레드, 문자열, 함수에 대한 고유한 문자열 식별자를 주죠. 다른 값(숫자, nil, 불리언)에 대해서는 이 지정자가 포인터 NULL을 나타내는 문자열을 만들어요.
string.gmatch (s, pattern [, init])
문자열 s 위에서 pattern(§6.4.1 참조)의 다음 캡처들을, 호출될 때마다 반환하는 반복자(iterator) 함수를 반환해요. 패턴이 캡처를 지정하지 않으면 매 호출에서 전체 일치가 생성돼요.
세 번째 옵션인 숫자 인자 init은 탐색을 시작할 위치를 지정해요. 기본값은 1이고 음수도 될 수 있어요.
예를 들어, 다음 루프는 문자열 s의 모든 단어를 반복하면서 한 줄에 하나씩 출력해요:
s = "hello world from Lua"
for w in string.gmatch(s, "%a+") do
print(w)
end
다음 예시는 주어진 문자열에서 key=value 형태의 모든 쌍을 테이블에 모아요:
t = {}
s = "from=world, to=Lua"
for k, v in string.gmatch(s, "(%w+)=(%w+)") do
t[k] = v
end
이 함수에서는 패턴 시작 부분의 캐럿 '^'이 앵커(anchor)로 동작하지 않아요. 그렇게 되면 반복이 진행되지 않기 때문이죠.
string.gsub (s, pattern, repl [, n])
pattern(§6.4.1 참조)의 모든(또는 n이 주어지면 처음 n개의) 발생을, repl이 지정한 대체 문자열로 치환한 s의 복사본을 반환해요. repl은 문자열, 테이블, 또는 함수가 될 수 있어요.
gsub는 두 번째 값으로 발생한 매치의 총 개수도 반환해요. 이름 gsub는 Global SUBstitution에서 왔어요.
repl이 문자열이면 그 값이 치환에 사용돼요. 여기서 문자 %는 이스케이프 문자로 동작해요. repl에서 %d(d는 1~9) 형태의 어떤 시퀀스든 d번째 캡처된 부분 문자열의 값을 뜻하고, %0는 전체 매치를, %%는 단일 % 하나를 뜻해요.
repl이 테이블이면 매 매치마다 테이블을 첫 번째 캡처를 키로 조회해요.
repl이 함수면 매치가 일어날 때마다 이 함수가 호출되는데, 캡처된 모든 부분 문자열이 순서대로 인자로 전달돼요.
어떤 경우든, 패턴이 캡처를 지정하지 않으면 전체 패턴이 캡처 안에 있는 것처럼 동작해요.
테이블 조회나 함수 호출이 반환한 값이 문자열이나 숫자면 그 값이 대체 문자열로 쓰여요. 만약 false나 nil이면 치환이 일어나지 않아요(즉, 원래 매치가 문자열에 유지돼요).
예시를 몇 개 볼게요:
x = string.gsub("hello world", "(%w+)", "%1 %1")
--> x="hello hello world world"
x = string.gsub("hello world", "%w+", "%0 %0", 1)
--> x="hello hello world"
x = string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1")
--> x="world hello Lua from"
x = string.gsub("home = $HOME, user = $USER", "%$(%w+)", os.getenv)
--> x="home = /home/roberto, user = roberto"
x = string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function (s)
return load(s)()
end)
--> x="4+5 = 9"
local t = {name="lua", version="5.4"}
x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t)
--> x="lua-5.4.tar.gz"
string.len (s)
문자열을 받아 그 길이를 반환해요. 빈 문자열 ""의 길이는 0이에요. 내장된 0도 세기 때문에 "a\000bc\000"의 길이는 5예요.
string.lower (s)
문자열을 받아, 모든 대문자를 소문자로 바꾼 복사본을 반환해요. 다른 문자들은 그대로 남아 있어요. 어떤 문자가 대문자인지의 정의는 현재 로케일(locale)에 따라 달라져요.
string.match (s, pattern [, init])
문자열 s에서 pattern(§6.4.1 참조)의 첫 번째 일치를 찾아요. 찾으면 match는 패턴의 캡처들을 반환하고, 그렇지 않으면 fail을 반환해요. 패턴이 캡처를 지정하지 않으면 전체 일치가 반환되죠.
세 번째 옵션인 숫자 인자 init은 탐색을 시작할 위치를 지정해요. 기본값은 1이고 음수도 될 수 있어요.
string.pack (fmt, v1, v2, ···)
값 v1, v2 등을 포맷 문자열 fmt(§6.4.2 참조)에 따라 이진 형태로 직렬화(패킹)한 이진 문자열을 반환해요.
string.packsize (fmt)
주어진 포맷으로 string.pack을 실행했을 때 나오는 문자열의 길이를 반환해요. 이 포맷 문자열은 가변 길이 옵션 's'나 'z'(§6.4.2 참조)를 가질 수 없어요.
string.rep (s, n [, sep])
문자열 s를 n번 붙인 문자열을, 그 사이사이에 sep를 넣어서 반환해요. sep의 기본값은 빈 문자열(즉, 구분자 없음)이에요. n이 양수가 아니면 빈 문자열을 반환해요.
(참고로, 이 함수를 한 번 호출하는 것만으로도 쉽게 머신의 메모리를 다 써버릴 수 있어요.)
string.reverse (s)
문자열 s를 뒤집은 문자열을 반환해요.
string.sub (s, i [, j])
s에서 i에서 시작해 j까지 계속되는 부분 문자열을 반환해요. i와 j는 음수일 수 있어요.
j가 없으면 -1과 같다고 가정해요(그게 문자열의 길이와 같죠). 특히 string.sub(s,1,j)는 길이 j인 s의 **접두사(prefix)**를, string.sub(s, -i)(양수 i에 대해)는 길이 i인 s의 **접미사(suffix)**를 반환해요.
음수 인덱스를 변환한 후 i가 1보다 작으면 1로 보정해요. j가 문자열 길이보다 크면 그 길이로 보정되죠. 이 보정 후에 i가 j보다 크면 함수는 빈 문자열을 반환해요.
string.unpack (fmt, s [, pos])
문자열 s에 패킹된 값들을 포맷 문자열 fmt(§6.4.2 참조)에 따라 반환해요 (즉 string.pack의 역연산). 옵션 pos는 s에서 읽기를 시작할 위치를 표시해요(기본값은 1). 읽은 값들 뒤에, 이 함수는 s에서 아직 읽지 않은 첫 바이트의 인덱스도 반환해요.
string.upper (s)
문자열을 받아, 모든 소문자를 대문자로 바꾼 복사본을 반환해요. 다른 문자들은 그대로 남아 있어요. 어떤 문자가 소문자인지의 정의는 현재 로케일에 따라 달라져요.
6.4.1 – 패턴 (Patterns)
루아의 패턴은 평범한 문자열로 표현되며, 패턴 매칭 함수 string.find, string.gmatch, string.gsub, string.match가 이를 패턴으로 해석해요. 이번 절에서는 이런 문자열의 문법과 의미(즉 무엇과 일치하는지)를 설명할게요.
문자 클래스 (Character Class): 문자 클래스는 문자들의 집합을 나타내는 데 사용돼요. 문자 클래스를 기술할 때 허용되는 조합은 다음과 같아요:
-
**x: ** (마법 문자
^$()%.[]*+-?가 아닌 어떤 x) 그 문자 x 자신을 나타내요. -
**
.: ** (점) 모든 문자를 나타내요. -
**
%a: ** 모든 알파벳 문자를 나타내요. -
**
%c: ** 모든 제어 문자를 나타내요. -
**
%d: ** 모든 숫자를 나타내요. -
**
%g: ** 공백을 제외한 모든 인쇄 가능한 문자를 나타내요. -
**
%l: ** 모든 소문자를 나타내요. -
**
%p: ** 모든 구두점(punctuation) 문자를 나타내요. -
**
%s: ** 모든 공백 문자를 나타내요. -
**
%u: ** 모든 대문자를 나타내요. -
**
%w: ** 모든 영숫자(alphanumeric) 문자를 나타내요. -
**
%x: ** 모든 16진수 숫자를 나타내요. -
**
%x: ** (영숫자가 아닌 어떤 x) 그 문자 x를 나타내요. 이것이 마법 문자를 이스케이프하는 표준 방법이에요. 영숫자가 아닌 어떤 문자든(구두점 문자를 포함해서, 심지어 마법이 아닌 것까지) 앞에 '%'를 붙여 패턴에서 그 자신을 나타낼 수 있어요. -
**
[set]: ** set에 있는 모든 문자의 합집합인 클래스를 나타내요. 문자 범위는 범위의 끝 문자들을 '-'로 분리해서 오름차순으로 지정할 수 있어요. 위에서 설명한 모든%x클래스들도 set의 구성 요소로 사용할 수 있어요. set의 다른 모든 문자는 그 자신을 나타내죠. 예를 들어[%w_](또는[_%w])는 영숫자 문자에 밑줄을 더한 집합을,[0-7]은 8진수 숫자를,[0-7%l%-]는 8진수 숫자에 소문자와 '-' 문자를 더한 집합을 나타내요.set에서 닫는 대괄호(
])를 넣으려면 그것을 set의 첫 번째 문자로 두면 돼요. 하이픈(-)을 넣으려면 그것을 set의 첫 번째나 마지막 문자로 두면 돼요. (두 경우 모두 이스케이프를 쓸 수도 있어요.)범위와 클래스 사이의 상호작용은 정의되어 있지 않아요. 그래서
[%a-z]나[a-%%]같은 패턴은 의미가 없어요. -
**
[^set]: ** set의 여집합(complement)을 나타내요. 여기서 set은 위와 같이 해석돼요.
단일 문자로 표현되는 모든 클래스(%a, %c 등)에 대해, 대문자로 된 대응 클래스는 그 클래스의 여집합을 나타내요. 예를 들어 %S는 모든 비공백 문자를 나타내죠.
문자, 공백, 그리고 다른 문자 그룹의 정의는 현재 로케일에 따라 달라져요. 특히 [a-z] 클래스는 %l과 동등하지 않을 수 있어요.
패턴 항목 (Pattern Item): 패턴 항목은 다음 중 하나가 될 수 있어요:
- 단일 문자 클래스 — 클래스 안의 어떤 단일 문자 하나와 일치해요;
- 단일 문자 클래스 뒤에 '
*' — 클래스 안의 문자가 0개 이상 이어지는 시퀀스와 일치해요. 이런 반복 항목은 항상 가장 긴 가능한 시퀀스와 일치해요; - 단일 문자 클래스 뒤에 '
+' — 클래스 안의 문자가 1개 이상 이어지는 시퀀스와 일치해요. 이런 반복 항목은 항상 가장 긴 가능한 시퀀스와 일치해요; - 단일 문자 클래스 뒤에 '
-' — 역시 클래스 안의 문자가 0개 이상 이어지는 시퀀스와 일치해요. 그러나 '*'와 달리 이런 반복 항목은 항상 가장 짧은 가능한 시퀀스와 일치해요; - 단일 문자 클래스 뒤에 '
?' — 클래스 안의 문자가 0개 또는 1개 나타나는 것과 일치해요. 가능하면 항상 1개의 발생과 일치해요; %n, 여기서 n은 1~9 — 이런 항목은 n번째 캡처된 문자열과 같은 부분 문자열과 일치해요(아래 참조);%bxy, 여기서 x와 y는 서로 다른 두 문자 — x로 시작하고 y로 끝나며, x와 y가 **균형(balanced)**을 이루는 문자열과 일치해요. 즉, 문자열을 왼쪽에서 오른쪽으로 읽으면서 x마다 +1, y마다 -1을 센다고 할 때, 끝나는 y는 그 카운트가 0에 도달하는 첫 번째 y예요. 예를 들어 항목%b()는 균형 잡힌 괄호를 가진 표현식과 일치해요.%f[set], 프론티어(frontier) 패턴 — 다음 문자가 set에 속하고 이전 문자는 set에 속하지 않는 어떤 위치에서든 빈 문자열과 일치해요. set은 앞에서 설명한 대로 해석돼요. 대상의 시작과 끝은 마치 '\0' 문자인 것처럼 처리돼요.
패턴 (Pattern): 패턴은 패턴 항목들의 시퀀스예요. 패턴의 시작 부분에 있는 캐럿 '^'은 대상 문자열의 시작에서 일치를 고정(앵커)해요. 패턴 끝의 '$'는 대상 문자열의 끝에서 일치를 고정해요. 다른 위치에서 '^'와 '$'는 특별한 의미가 없고 그 자신을 나타내요.
캡처 (Captures): 패턴은 괄호로 둘러싸인 하위 패턴을 포함할 수 있어요. 이것들이 캡처를 기술하죠. 일치가 성공하면 대상 문자열에서 캡처와 일치하는 부분 문자열들이 나중에 쓸 수 있게 저장(캡처)돼요. 캡처는 왼쪽 괄호 순서대로 번호가 매겨져요. 예를 들어 패턴 "(a*(.)%w(%s*))"에서 "a*(.)%w(%s*)"와 일치하는 부분은 첫 번째 캡처로 저장되어 번호 1을 가지고, "."에 일치하는 문자는 번호 2로, "%s*"에 일치하는 부분은 번호 3을 가져요.
특별한 경우로, 캡처 ()는 **현재 문자열 위치(숫자)**를 캡처해요. 예를 들어 패턴 "()aa()"를 문자열 "flaaap"에 적용하면 3과 5라는 두 개의 캡처가 있어요.
다중 매치 (Multiple matches): string.gsub 함수와 string.gmatch 반복자는 대상에서 주어진 패턴의 여러 발생을 매치해요. 이 함수들에서는 새 매치는 이전 매치의 끝에서 최소 1바이트 뒤에 끝나야만 유효한 것으로 간주돼요. 다시 말해, 패턴 기계는 다른 매치 바로 다음에 빈 문자열을 매치로 절대 받아들이지 않아요. 예를 들어 다음 코드의 결과를 볼게요:
> string.gsub("abc", "()a*()", print);
--> 1 2
--> 3 3
--> 4 4
두 번째와 세 번째 결과는 루아가 'b' 뒤와 'c' 뒤에서 빈 문자열을 매치해서 나온 거예요. 루아는 'a' 뒤에서는 빈 문자열을 매치하지 않는데, 그건 이전 매치와 같은 위치에서 끝나기 때문이에요.
6.4.2 – Pack과 Unpack을 위한 포맷 문자열 (Format Strings for Pack and Unpack)
string.pack, string.packsize, string.unpack의 첫 번째 인자는 포맷 문자열이에요. 이게 생성하거나 읽는 구조의 배치(layout)를 기술하죠.
포맷 문자열은 변환 옵션들의 시퀀스예요. 변환 옵션은 다음과 같아요:
- **
<: ** 리틀 엔디언(little endian) 설정 - **
>: ** 빅 엔디언(big endian) 설정 - **
=: ** 네이티브 엔디언(native endian) 설정 - **
![n]: ** 최대 정렬(alignment)을n으로 설정 (기본은 네이티브 정렬) - **
b: ** 부호 있는 바이트 (char) - **
B: ** 부호 없는 바이트 (char) - **
h: ** 부호 있는short(네이티브 크기) - **
H: ** 부호 없는short(네이티브 크기) - **
l: ** 부호 있는long(네이티브 크기) - **
L: ** 부호 없는long(네이티브 크기) - **
j: **lua_Integer - **
J: **lua_Unsigned - **
T: **size_t(네이티브 크기) - **
i[n]: **n바이트의 부호 있는int(기본은 네이티브 크기) - **
I[n]: **n바이트의 부호 없는int(기본은 네이티브 크기) - **
f: **float(네이티브 크기) - **
d: **double(네이티브 크기) - **
n: **lua_Number - **
cn: **n바이트의 고정 크기 문자열 - **
z: ** 0으로 끝나는 문자열 - **
s[n]: ** 길이를 앞에 붙인 문자열. 길이는n바이트의 부호 없는 정수로 코딩돼요 (기본은size_t) - **
x: ** 패딩(padding) 1바이트 - **
Xop: ** 옵션op에 따라 정렬하는 빈 항목 (그 외에는 무시돼요) - **'
': ** (공백) 무시
("[n]" 은 옵션인 정수 숫자를 뜻해요.)
패딩, 공백, 그리고 설정("xX <=>!" 옵션)을 제외하면, 각 옵션은 string.pack의 인자 하나 또는 string.unpack의 결과 하나에 대응해요.
옵션 "!n", "sn", "in", "In"에서는 n이 1에서 16 사이의 어떤 정수든 될 수 있어요.
모든 정수 옵션은 오버플로우를 검사해요. string.pack은 주어진 값이 주어진 크기에 맞는지 검사하고, string.unpack은 읽은 값이 루아 정수에 맞는지 검사해요. 부호 없는 옵션에서는 루아 정수도 부호 없는 값으로 취급돼요.
어떤 포맷 문자열이든 "!1="이 앞에 붙은 것처럼 시작해요. 즉 최대 정렬 1(정렬 없음)과 네이티브 엔디언으로 시작하죠.
네이티브 엔디언은 전체 시스템이 빅 또는 리틀 엔디언 중 하나라고 가정해요. 패킹 함수들은 혼합 엔디언 포맷의 동작을 올바르게 에뮬레이트하지 못해요.
정렬은 다음과 같이 동작해요. 각 옵션에 대해, 데이터가 옵션 크기와 최대 정렬 중 작은 값의 배수인 오프셋에서 시작할 때까지 포맷에 추가 패딩이 들어가요. 이 작은 값은 2의 거듭제곱이어야 해요. 옵션 "c"와 "z"는 정렬되지 않고, 옵션 "s"는 시작 정수(시작 부분의 정수)의 정렬을 따라가요.
모든 패딩은 string.pack에 의해 0으로 채워지고, string.unpack에 의해 무시돼요.
6.5 – UTF-8 지원 (UTF-8 Support)
이 라이브러리는 UTF-8 인코딩에 대한 기본적인 지원을 제공해요. 모든 함수를 utf8 테이블 안에 둬요.
중요한 점이 있어요. 이 라이브러리는 인코딩 처리 외에는 유니코드에 대한 어떤 지원도 하지 않아요. 문자 분류처럼 문자의 의미가 필요한 어떤 연산도 이 라이브러리의 범위 밖이에요.
달리 명시되지 않는 한, 바이트 위치를 인자로 기대하는 모든 함수는 주어진 위치가 바이트 시퀀스의 시작이거나 대상 문자열 길이에 1을 더한 것이라고 가정해요. string 라이브러리처럼, 음수 인덱스는 문자열의 끝에서부터 셉니다.
바이트 시퀀스를 만드는 함수들은 원래 UTF-8 명세에 정의된 대로 0x7FFFFFFF까지의 모든 값을 받아들여요. 그건 최대 6바이트 바이트 시퀀스를 뜻해요.
바이트 시퀀스를 해석하는 함수들은 유효한(잘 형성되고 길지 않은) 시퀀스만 받아들여요. 기본적으로 유효한 유니코드 코드 포인트를 만드는 바이트 시퀀스만 받아들이고, 10FFFF보다 큰 값과 서로게이트(surrogate)는 거부해요. 불리언 인자 lax가 있으면 이 검사들을 풀어서 0x7FFFFFFF까지의 모든 값을 받아들여요. (잘 형성되지 않았거나 과도하게 긴 시퀀스는 여전히 거부돼요.)
utf8.char (···)
0개 이상의 정수를 받아, 각각을 그에 대응하는 UTF-8 바이트 시퀀스로 변환하고, 이 모든 시퀀스를 이어붙인 문자열을 반환해요.
utf8.charpattern
패턴(함수가 아니라 문자열) "[\0-\x7F\xC2-\xFD][\x80-\xBF]*"(§6.4.1 참조)이에요. 대상이 유효한 UTF-8 문자열이라고 가정할 때, 정확히 하나의 UTF-8 바이트 시퀀스와 일치해요.
utf8.codes (s [, lax])
값들을 반환해서, 문자열 s의 모든 UTF-8 문자를 반복할 수 있게 해줘요. 여기서 p는 각 문자의 위치(바이트 단위), c는 코드 포인트예요:
for p, c in utf8.codes(s) do body end
유효하지 않은 바이트 시퀀스를 만나면 오류를 일으켜요.
utf8.codepoint (s [, i [, j [, lax]]])
s에서 바이트 위치 i와 j 사이에 시작하는(둘 다 포함) 모든 문자의 코드 포인트를 (정수로) 반환해요. i의 기본값은 1, j의 기본값은 i예요. 유효하지 않은 바이트 시퀀스를 만나면 오류를 일으켜요.
utf8.len (s [, i [, j [, lax]]])
문자열 s에서 위치 i와 j 사이에 시작하는(둘 다 포함) UTF-8 문자의 개수를 반환해요. i의 기본값은 1, j의 기본값은 -1이에요. 유효하지 않은 바이트 시퀀스를 찾으면 fail에 첫 번째 잘못된 바이트의 위치를 더해 반환해요.
utf8.offset (s, n [, i])
s의 n번째 문자(위치 i에서부터 셈)의 인코딩이 시작되는 **위치(바이트 단위)**를 반환해요. 음수 n은 위치 i 앞의 문자들을 가져와요.
i의 기본값은 n이 음이 아닐 때 1이고, 그 외에는 #s + 1이에요. 그래서 utf8.offset(s, -n)은 문자열 끝에서 n번째 문자의 오프셋을 가져와요.
지정된 문자가 대상 안에 없거나 대상의 끝 바로 뒤에 있지 않으면 함수는 fail을 반환해요.
특별한 경우로, n이 0이면 함수는 s의 i번째 바이트를 포함하는 문자의 인코딩 시작을 반환해요.
이 함수는 s가 유효한 UTF-8 문자열이라고 가정해요.
더 알아보기
- 문자열 라이브러리의 더 넓은 그림 — 이번 절에서 다룬 함수들은 문자열을 찾고, 자르고, 치환하는 데 집중했어요. 루아에서 문자열을 다루는 더 많은 컨텍스트(예: 길이 연산자
#, 문자열 연결..,tostring)를 보려면 기본 개념과 표현식 관련 섹션을 함께 보면 좋아요. - 패턴 매칭의 실무 —
%b(균형 괄호),%f(프론티어), 캡처 번호 매기기처럼 이번 절에서 소개한 기호들이 실제 코드에서 어떻게 쓰이는지 예제를 직접 돌려보면 감이 와요. 특히gmatch와gsub는 텍스트 처리에서 정말 자주 쓰이니 직접 실습해 보는 걸 추천해요. - UTF-8과 문자열의 관계 — §6.4에서 문자열 라이브러리는 1바이트 문자 인코딩을 가정한다고 했죠. 멀티바이트 문자(한글 포함)를 다룰 때는 이
utf8라이브러리가 문자열의 위치를 바이트 단위로 세는 걸 기억해야 해요. 멀티바이트 처리에서는string함수보다utf8함수를 쓰는 게 안전해요. - 공식 문서 — Lua 5.4 Reference Manual 전체는 https://www.lua.org/manual/5.4/manual.html 에 있어요. 이 절의 원문(§6.4, §6.5)과 패턴/포맷 문자열에 대한 정확한 정의를 확인하고 싶다면 직접 찾아보는 걸 권해요.