{ }codelper

배우기 › TypeScript

데이터 · 11

문자 하나 다루기

글자 하나를 값으로: 코드, 글자 계산, i번째 글자, 되풀이.

글자 하나도 값이에요.
속으로는 수(코드)라서 더하고 뺄 수 있어요.
아래 예제는 소문자 하나와 숫자 하나가 번갈아 나오는 c2a3z1 같은 문자열을 두 글자씩 읽어서, 글자와 그 코드, 알파벳에서 다음 글자, 그리고 그 글자를 숫자만큼 되풀이한 문자열을 한 줄씩 찍어요.

import { readFileSync } from "node:fs";const s: string = readFileSync(0, "utf8").trim();const a: number = "a".charCodeAt(0);for (let i = 0; i < s.length; i += 2) {  const ch: string = s[i]; // i번째 글자  const count: number = Number(s[i + 1]); // 숫자 글자 → 수  const code: number = ch.charCodeAt(0);  const after: string = String.fromCharCode(a + ((code - a + 1) % 26)); // 다음 글자(z 다음은 a)  console.log(ch, code, after, ch.repeat(count));}
stdin
c2a3z1
stdout
c 99 d cc
a 97 b aaa
z 122 a z

핵심

  • C·C++·Java·C#에는 글자 하나만 담는 타입 char가 있고, 작은따옴표로 써요('a').
    Python·JavaScript·크레이콘에는 따로 없어서, 글자 하나도 길이가 1인 문자열이에요.
  • 글자 계산: 알파벳의 코드는 차례대로 이어져 있어요(a 97, b 98, …).
    그래서 ch - 'a'는 a에서 몇 칸 뒤인지(0~25)이고, 'a' + k는 a에서 k칸 뒤 글자예요.
    char가 없는 언어는 코드로 바꿔 계산한 뒤 글자로 되돌려요.
  • 숫자 글자는 수가 아니에요.
    '7'의 코드는 55예요.
    수 7을 얻으려면 '0'의 코드를 빼거나('7' - '0'), 수로 바꾸는 함수를 써요.
  • i번째 글자는 s[i]로 꺼내요(Java는 s.charAt(i)).
    마지막 글자는 길이 - 1번이에요.
    그 너머를 읽으면 오류로 멈추는 언어, 조용히 undefined를 주는 언어, 아예 확인하지 않아서 무슨 일이 생길지 정해져 있지 않은 언어(C·C++)로 갈려요.
  • 되풀이는 같은 문자열을 n번 이어 붙여요.
    "ab"를 3번이면 "ababab", 0번이면 빈 문자열이에요.
    별 찍기나 줄 긋기를 반복문 없이 쓸 수 있어요.

언어별로 쓰는 법

Python코드 ord(ch), 글자로 chr(n) — 다음 글자는 chr(ord(ch) + 1).
숫자 글자는 int(ch).
s[i], 뒤에서부터는 s[-1].
범위 밖은 IndexError.
되풀이는 ch * 3
JavaScript코드 ch.charCodeAt(0), 글자로 String.fromCharCode(n).
숫자 글자는 Number(ch).
s[i], 뒤에서부터는 s.at(-1).
범위 밖 s[9]는 오류 없이 undefined.
되풀이는 ch.repeat(3)
TypeScriptJavaScript와 같아요
Javachar ch = s.charAt(i) — s[i]는 못 써요.
코드는 int code = ch, 글자로는 (char) (ch + 1).
숫자 글자는 ch - '0'.
범위 밖은 StringIndexOutOfBoundsException.
되풀이는 문자열에만 있어서 String.valueOf(ch).repeat(3)
C#char ch = s[i], 마지막은 s[^1].
코드는 int code = ch, 글자로는 (char)(ch + 1).
숫자 글자는 ch - '0'.
범위 밖은 IndexOutOfRangeException.
글자 되풀이는 new string(ch, 3)
C++char ch = s[i] — char가 곧 작은 정수라서 ch + 1, ch - '0'을 바로 써요.
s[i]는 범위를 확인하지 않아서, 길이를 넘는 번호를 읽으면 정의되지 않은 동작이에요(엉뚱한 값이 나오거나 죽을 수 있어요).
s.at(i)는 확인해서 std::out_of_range를 던져요.
글자 되풀이는 std::string(3, ch)(개수가 먼저)
Cchar ch = s[i] — C++처럼 char가 작은 정수예요.
printf에서 %c는 글자로, %d는 코드로 찍어요.
s[길이]에는 끝 표시 \0이 있고, 그 너머를 읽는 것은 확인도 없고 정의되지 않은 동작이에요(엉뚱한 값이 나오거나 죽을 수 있어요).
되풀이는 없어서 반복문으로 찍어요
Craykon코드 글자.코드(ch), 글자로 글자.글자로변환(n).
숫자 글자는 ch.수로변환().
s[i] — 범위 밖은 "문자열에는 0~2번만 있어요" 오류(음수 번호도 마찬가지).
되풀이는 ch.되풀이(3)

자주 하는 실수

문자열 글자에 1을 더했어요Python · JavaScript · TypeScript · Craykon

print("a" + 1)

→ Python은 TypeError: can only concatenate str (not "int") to str. JavaScript·TypeScript·크레이콘은 오류 없이 a1 — 수를 뒤에 이어 붙여요

코드로 바꿔 더한 뒤 글자로 되돌려요: Python chr(ord("a") + 1), JavaScript String.fromCharCode("a".charCodeAt(0) + 1), 크레이콘 글자.글자로변환(글자.코드("a") + 1).

글자에 1을 더해 찍었더니 수가 나왔어요Java · C# · C++

System.out.println('a' + 1);   // Java
Console.WriteLine('a' + 1);    // C#
std::cout << 'a' + 1;          // C++

→ 셋 다 98 — char에 수를 더하면 결과는 int예요

글자로 되돌려요: (char) ('a' + 1) → b.
Java·C#에서 char c = 'a'; c = c + 1;은 같은 이유로 컴파일 오류라서, c++나 c = (char) (c + 1)로 써요.

숫자 글자를 그대로 수로 썼어요C · C++ · Java · C#

char ch = '7';
int n = ch;
printf("%d\n", n);

→ 55 — 7이 아니라 '7'의 코드예요

'0'을 빼요: ch - '0' → 7.
숫자 글자의 코드가 '0'(48)부터 차례로 이어져 있어서 돼요.

언어마다 다른 점
되풀이는 곱셈과 헷갈리기 쉬워요.
Python에서 "3" * 2는 6이 아니라 "33"이라서, 입력을 수로 바꾸지 않고 곱하면 문자열 되풀이가 돼요.
JavaScript는 반대로 "3" * 2가 6이고, TypeScript는 컴파일 오류, 크레이콘은 "* 계산은 수끼리만 할 수 있어요"라며 멈춰요.
C++ std::string(3, '*')는 개수가 먼저예요.
순서를 바꿔 std::string('*', 3)으로 쓰면 오류 없이 엉뚱한 42글자짜리 문자열이 돼요('*'의 코드가 42).

연습 문제