scan — sscanf 스타일의 변환 지정자로 문자열 파싱
scan — sscanf 스타일의 변환 지정자로 문자열 파싱
입력 문자열에서 숫자나 특정 형식의 조각들을 뽑아내고 싶을 때가 있어요. scan은 ANSI C의 sscanf처럼 % 변환 지정자를 사용해 문자열을 파싱하는 명령이에요. 성공한 변환 개수를 반환하고, 각각의 파싱 결과를 변수에 담아요.
출처: 문서
본문
문법
scan string format ?varName varName ...?
동작 방식
이 명령은 ANSI C의 sscanf 프로시저와 비슷한 방식으로 입력 문자열에서 부분 문자열을 파싱하고, 수행된 변환 개수를 반환해요. 어떤 변환도 수행되기 전에 입력 문자열의 끝에 도달하면 -1을 반환해요. string은 파싱할 입력이고, format은 sscanf에서처럼 % 변환 지정자를 사용해 어떻게 파싱할지 나타내요. 각 varName은 변수의 이름을 주는데, 변환 지정자와 일치하는 부분 문자열이 string에서 스캔되면 그 부분 문자열이 해당 변수에 할당돼요.
varName 변수가 하나도 지정되지 않으면, scan은 인라인 방식으로 동작해 그렇지 않으면 변수에 저장될 데이터를 리스트로 반환해요. 인라인인 경우, 어떤 변환도 수행되기 전에 입력 문자열의 끝에 도달하면 빈 문자열이 반환돼요.
스캔 방식
scan은 string과 format을 함께 스캔하면서 동작해요. format의 다음 문자가 공백이나 탭이면 string에서 임의 개수의 공백 문자(0 포함)와 일치해요. 그 외에 % 문자가 아니면 string의 다음 문자와 일치해야 해요.
format에서 %를 만나면 변환 지정자의 시작을 나타내요. 변환 지정자는 % 뒤에 최대 네 개의 필드를 포함해요: XPG3 위치 지정자(또는 변환 값을 변수에 할당하는 대신 버릴 것을 나타내는 *), 최대 부분 문자열 너비를 나타내는 숫자, 크기 수정자(size modifier), 그리고 변환 문자예요. 이 필드들은 변환 문자를 제외하고 모두 선택이에요. 존재하는 필드들은 위 순서대로 나타나야 해요.
scan이 format에서 변환 지정자를 찾으면, 먼저 string에서 공백 문자들을 건너뛰어요 (변환 문자가 [나 c인 경우 제외). 그런 다음 변환 지정자에 따라 다음 입력 문자들을 변환하고, 그 결과를 scan의 다음 인자가 주는 변수에 저장해요.
선택적 위치 지정자(OPTIONAL POSITIONAL SPECIFIER)
% 뒤에 십진 숫자와 $가 오면(예: "%2$d"), 사용할 변수는 다음 순차 인자에서 가져오지 않아요. 대신 숫자가 나타내는 인자에서 가져오는데, 1이 첫 번째 varName에 해당해요. format에 위치 지정자가 있으면 모든 지정자가 위치 지정자여야 해요. 인자 리스트의 모든 varName은 정확히 하나의 변환 지정자에 해당해야 하며, 그렇지 않으면 오류가 생성돼요. 인라인 경우에는 어떤 위치든 최대 한 번 지정할 수 있고, 빈 위치는 빈 문자열로 채워져요.
선택적 크기 수정자(OPTIONAL SIZE MODIFIER)
크기 수정자 필드는 부분 문자열을 Tcl의 정수 값 중 하나로 스캔할 때만 사용돼요. 크기 수정자 필드는 변수에(또는 인라인 경우 결과 리스트의 위치에) 저장하기에 허용되는 정수 범위를 지시해요. 문법상 유효한 크기 수정자 값은 h, L, l, ll이에요. h는 크기 수정자가 없는 것과 같아요. 둘 다 저장할 정수 범위가 expr 명령의 int() 함수가 만드는 범위로 제한됨을 나타내요. L은 l과 같아요. 둘 다 저장할 정수 범위가 expr 명령의 wide() 함수가 만드는 범위로 제한됨을 나타내요. ll은 저장할 정수 범위가 무제한임을 나타내요.
필수 변환 문자(MANDATORY CONVERSION CHARACTER)
지원되는 변환 문자:
d— 입력 부분 문자열은 십진 정수여야 해요. 읽어서 크기 수정자 값에 따라 잘라 정수 값을 변수에 저장해요.o— 입력 부분 문자열은 8진 정수여야 해요. 정수 값으로 저장해요 (크기 수정자에 따라 잘림).x또는X— 입력 부분 문자열은 16진 정수여야 해요. 정수 값으로 저장해요 (잘림).b— 입력 부분 문자열은 이진 정수여야 해요. 정수 값으로 저장해요 (잘림).u— 입력 부분 문자열은 십진 정수여야 해요. 정수 값은 크기 수정자에 따라 잘리고, 잘린 범위에 대한 해당 부호 없는 값이 계산되어 십진 문자열로 변수에 저장돼요. 잘림 범위에 대한 언급 없이는 의미가 없으므로, 크기 수정자ll은 변환 문자u와 함께 허용되지 않아요.i— 입력 부분 문자열은 정수여야 해요. 진법(즉 십진, 8진, 16진)은 C 규칙(8진은 앞 0, 16진은 접두사 0x)으로 결정돼요. 정수 값으로 저장돼요 (잘림).c— 단일 문자를 읽어 그 Unicode 값을 정수 값으로 변수에 저장해요. 이 경우 초기 공백은 건너뛰지 않으므로 입력 부분 문자열은 공백 문자일 수 있어요.s— 입력 부분 문자열은 다음 공백 문자까지의 모든 문자로 구성돼요. 문자들이 변수에 복사돼요.e또는f또는g또는E또는G— 입력 부분 문자열은 부동소수점 숫자여야 해요. 선택적 부호, 어쩌면 소수점을 포함하는 십진 숫자 문자열, 그리고e나E뒤에 선택적 부호와 십진 숫자 문자열이 오는 선택적 지수로 구성돼요. 읽어서 부동소수점 값으로 변수에 저장해요.[chars]— 입력 부분 문자열은chars안의 하나 이상의 문자로 구성돼요. 일치하는 문자열이 변수에 저장돼요. 대괄호 사이의 첫 문자가]면 집합의 닫는 대괄호가 아니라chars의 일부로 취급돼요.chars에a-b형태의 시퀀스가 있으면a와b사이(포함)의 어떤 문자든 일치해요. 대괄호 사이의 첫 또는 마지막 문자가-면 범위를 나타내는 게 아니라chars의 일부로 취급돼요.[^chars]— 입력 부분 문자열은chars에 없는 하나 이상의 문자로 구성돼요. 일치하는 문자열이 변수에 저장돼요.^바로 다음 문자가]면 집합의 닫는 괄호가 아니라 집합의 일부로 취급돼요.chars에a-b형태가 있으면 그 사이(포함)의 어떤 문자든 집합에서 제외돼요.n— 입력 문자열에서 어떤 입력도 소비하지 않아요. 대신 지금까지 입력 문자열에서 스캔한 총 문자 수를 변수에 저장해요.
변환을 위한 입력에서 읽는 문자 수는 그 특정 변환에 의미가 있는 최대 수예요 (예: %d는 가능한 한 많은 십진 숫자, %o는 가능한 한 많은 8진 숫자). 주어진 변환의 입력 부분 문자열은 공백 문자를 만나거나 최대 부분 문자열 너비에 도달했을 때 끝나는데, 둘 중 먼저 오는 쪽이에요. 변환 지정자에 *가 있으면 변수가 할당되지 않고 다음 scan 인자가 소비되지 않아요.
ANSI SSCANF와의 차이점
scan 명령의 동작은 다음 차이점을 제외하고 ANSI C sscanf 프로시저와 같아요:
%p변환 지정자는 지원되지 않아요.%c변환의 경우 단일 문자 값이 십진 문자열로 변환된 다음 해당varName에 할당돼요. 이 변환에는 부분 문자열 너비를 지정할 수 없어요.h수정자는 항상 무시되고, 실수 값을 변환할 때l과L수정자는 무시돼요 (즉 내부 표현에double타입이 사용됨).ll수정자는sscanf에 대응하는 것이 없어요.- 어떤 변환도 수행되기 전에 입력 문자열의 끝에 도달하고 변수가 주어지지 않으면 빈 문자열이 반환돼요.
예제
UNICODE 문자를 숫자 값으로 변환하기:
set char "x"
set value [scan $char %c]
#RRGGBB 형태의 색상 지정을 부분 문자열 크기와 함께 16진 변환으로 파싱하기:
set string "#08D03F"
scan $string "#%2x%2x%2x" r g b
HH:MM 형태의 시간 문자열 파싱하기. 이 방식이 8진수 문제를 피하면서 십진으로 강제한다는 점에 주목해요 (%i 변환이었다면 신경쓰지 않았을 때만):
set string "08:08" ;# *아님* 8진!
if {[scan $string "%d:%d" hours minutes] != 2} {
error "not a valid time string"
}
# 숫자 범위는 우리가 이해해야 한다...
if {$minutes < 0 || $minutes > 59} {
error "invalid number of minutes"
}
문자열을 비공백 문자 시퀀스로 쪼개기 (%n 변환을 사용해 앞 공백 건너뛰기를 올바르게 처리하는 점에 주목):
set string " a string {with braced words} + leading space "
set words {}
while {[scan $string %s%n word length] == 2} {
lappend words $word
set string [string range $string $length end]
}
간단한 좌표 문자열을 파싱하고 종료 문자를 명시적으로 찾아 완전성을 검사하기:
set string "(5.2,-4e-2)"
# scan 패턴의 리터럴 부분 앞의 공백이 의미 있고,
# ")"가 Unicode 문자 \u0029라는 점에 주목
if {
[scan $string " (%f ,%f %c" x y last] != 3
|| $last != 0x0029
} then {
error "invalid coordinate string"
}
puts "X=$x, Y=$y"
크기 수정자에 따른 정수 잘림을 보여주는 대화형 세션:
% set tcl_platform(wordSize)
4
% scan 20000000000000000000 %d
2147483647
% scan 20000000000000000000 %ld
9223372036854775807
% scan 20000000000000000000 %lld
20000000000000000000
더 알아보기
format:scan의 역방향인 형식화된 출력 생성sscanf: ANSI C 파싱 함수