텍스트 / 16진수 변환
텍스트를 16진수로, 16진수를 텍스트로 변환합니다. UTF-8 또는 UTF-16 바이트를 공백 구분, 배열, 이스케이프, 16진수 덤프로 쓰고, 어느 형태든 텍스트로 되돌립니다.
EC 95 88 EB 85 95 ED 95 98 EC 84 B8 EC 9A 94
문자열을, 그것이 저장된 바이트로 보기
프로그램이 다루는 모든 문자열은 그 아래에서는 바이트가 늘어선 줄이고, 이 페이지는 그 바이트를 16진수로, 한 바이트에 두 자리씩 적어 냅니다. Hello를 입력하면 48 65 6C 6C 6F가 나오는데, 글자 하나에 한 바이트이고 마지막을 뺀 각 바이트 뒤에 공백이 옵니다. 대신 ‘16진수에서 텍스트로’ 방향으로 바꾸고 로그나 데이터베이스나 디버거에서 가져온 16진수를 붙여넣으면, 그 바이트가 담고 있는 텍스트를 돌려받습니다.
같은 텍스트도 인코딩이 다르면 바이트가 달라지고, 이 페이지의 숫자가 무엇인지는 ‘단위’가 정합니다. 시작은 웹의 인코딩인 UTF-8이며, 여기서 H는 바이트 48이고 א는 바이트 D7 90입니다. UTF-16 LE와 UTF-16 BE는 두 글자 모두에 2바이트를 쓰되 순서가 서로 반대입니다 — H라면 48 00 또는 00 48. 반면 ‘코드 포인트’는 바이트를 아예 빼고 유니코드가 매긴 번호를 쓰며, א라면 U+05D0입니다. 어떤 단위가 설정되어 있든 읽는 16진수에도 쓰는 텍스트와 똑같이 적용되고, 그대로 유지됩니다. 붙여넣은 16진수가 다른 단위의 것과 닮았으면 페이지가 그 단위를 권할 수는 있지만, 단위를 옮기는 것은 여러분의 클릭뿐입니다.
바이트 하나에 16진수 두 자리
1바이트는 256가지 값, 곧 0부터 255까지 가운데 하나를 담습니다. 16진수는 16씩 세며, 0부터 9까지의 숫자에 이어 10부터 15까지는 A부터 F를 쓰고, 16의 16배는 256이므로 16진수 두 자리면 모든 바이트를 나타낼 수 있고 셋째 자리는 필요 없습니다. 00은 0, 7F는 127, FF는 255입니다. H는 72, 곧 16이 넷에 8이므로 그 바이트는 48입니다.
16진수 한 자리는 정확히 4비트, 곧 반 바이트를 나타냅니다. 48의 4는 0100이고 8은 1000이며, 나란히 놓으면 H의 8비트인 01001000이 됩니다. 페이지는 모든 바이트의 두 자리를 다 남기므로 줄바꿈은 0A, 공백은 20이며, 모든 바이트가 같은 폭을 차지하므로 16진수는 바이트 사이에 아무것도 없어도 되읽을 수 있습니다. 4869는 Hi입니다.
16진수의 글자는 대문자든 소문자든 뜻이 같습니다. 페이지는 ‘소문자’를 고르지 않는 한 대문자로 쓰므로 é는 C3 A9 또는 c3 a9가 되고, 되읽을 때는 둘 다 받아들이며 한 번에 붙여넣은 것 안에 섞여 있어도 괜찮습니다.
붙여넣을 곳에 맞춘 다섯 가지 스타일
16진수에서 ‘스타일’은 같은 바이트를 다섯 가지로 늘어놓으며, 저마다 16진수가 다음에 갈 곳을 위한 것입니다. Hi, 곧 두 바이트 48과 69라면 다음과 같습니다.
- ‘공백 구분’은 48 69로, 바이트 사이에 공백을 둡니다. 읽고 세기에 가장 쉽고, 페이지가 처음 열릴 때의 스타일입니다.
- ‘구분 없음’은 4869로, 숫자를 이어 붙이며, 값을 16진수 문자열 하나로 받는 입력란이나 매개변수를 위한 것입니다.
- ‘배열’은 0x48, 0x69로, 바이트마다 앞에 0x가 붙은 수가 되고 사이에 쉼표가 들어가며, C, C#, JavaScript, Python, Go의 바이트 배열에 바로 붙여넣을 수 있습니다.
- ‘이스케이프’는 \x48\x69로, 바이트마다 \x와 그 두 자리로 쓰며, C 문자열이나 b'\x48\x69' 같은 Python 바이트 리터럴 안에서 바이트를 쓰는 방식입니다.
- ‘16진수 덤프’는 바이트를 여러 줄로 늘어놓고, 각 줄이 어디서 시작하는지와 같은 바이트를 텍스트로 옆에 붙입니다. 다음 절에서 하나를 읽어 봅니다.
되읽기는 다섯 가지를 모두 받아들이며, 바이트를 건드리지 않는 다른 서식도 받아들입니다. 48:69 같은 콜론, 48-69처럼 .NET의 BitConverter.ToString이 쓰는 하이픈, 각 바이트 앞의 0x나 0X, 그리고 붙여넣은 전체를 한 번 감싼 소괄호, 대괄호, 중괄호나 따옴표입니다. 스타일과 대소문자는 쓸 때에만 의미가 있으므로, 방향이 ‘16진수에서 텍스트로’일 때는 두 설정이 모두 사라집니다.
‘이스케이프’에는 함정이 하나 있습니다. JavaScript 문자열이나, 바이트 리터럴이 아닌 보통의 Python 문자열 안에서 \x는 바이트가 아니라 문자를 나타내므로, 거기서 \xD7\x90은 문자 두 개이지 UTF-8 바이트가 D7 90인 א가 아닙니다. ASCII를 넘어서면 바이트는 바이트를 받는 곳에 넘기세요.
16진수 덤프를 한 열씩 읽기
16진수 덤프는 바이트를 한 줄에 열여섯 개씩 늘어놓고, 양쪽에 그것을 찾는 데 도움이 되는 열을 둡니다. ‘16진수 덤프’로 쓰면 Hello, World!와 줄바꿈이 한 줄을 채웁니다. 먼저 00000000은 오프셋으로, 그 줄의 첫 바이트가 0부터 세어 어디에 있는지를 16진수 여덟 자리로 나타냅니다. 다음은 열네 개의 바이트로, 여덟 개와 여섯 개로 나뉘고 두 절반 사이는 다른 곳보다 넓게 띕니다. 그다음 |Hello, World!.| 열은 같은 바이트를 문자로 보인 것이며, 인쇄 가능한 ASCII가 아닌 바이트는 줄바꿈을 포함해 모두 점으로 나타납니다. 마지막 줄에는 0000000E만 있는데, 이는 14, 곧 다음 바이트가 올 자리이므로 길이입니다. 이것이 hexdump -C가 출력하는 형식입니다.
- ‘16진수에서 텍스트로’를 고르고 단위가 ‘코드 포인트’가 아니면, 붙여넣은 16진수 덤프는 바이트만 읽힙니다. 텍스트 열은 읽기에서 빠지고, 어떤 오프셋도 바이트로 읽히지 않으며, 결과 아래의 알림이 입력을 16진수 덤프로 읽었다고 말하고 그 형식을 밝힙니다.
- 그렇게 읽히는 형식은 두 가지입니다. hexdump -C의 형식과, xxd가 옵션 없이 출력하는 형식으로, 후자에서는 각 오프셋 뒤에 콜론이 오고 바이트가 네 자리 그룹으로 놓입니다. xxd -p가 출력하는 맨 16진수는 형식이 필요 없고 다른 16진수와 똑같이 읽힙니다.
- hexdump -C는 위 줄과 같은 내용을 되풀이하는 줄들 대신 *만 있는 줄을 출력하고, 페이지는 아래 오프셋을 보고 그 줄들을 다시 채워 넣으므로 바이트가 빠짐없이 돌아옵니다.
- 첫 번째를 뺀 모든 오프셋은 hexdump -C의 마지막 줄에 있는 길이까지 포함해 그 위의 바이트에서 이어져야 하며, 그렇지 않은 줄은 잘못 읽히는 대신 거기서 거부됩니다. 빠진 줄, 잃어버린 바이트, 잘못 입력한 오프셋이 드러나는 곳이 바로 거기입니다. 뒤에 오프셋이 오지 않는 부분은 확인할 수 없으므로, 첫 몇 줄이 빠진 16진수 덤프나, 뒤에 길이 줄이 없는 끝부분 — xxd는 그런 줄을 쓰지 않습니다 — 은 남은 그대로 읽힙니다.
UTF-16 LE, 그리고 두 번째 바이트마다 00인 이유
Windows는 텍스트를 하위 바이트가 먼저 오는 UTF-16으로 보관하며, 이것이 UTF-16 LE입니다. .NET에서는 Encoding.Unicode가 이것이고, SQL Server도 NVARCHAR 값을 같은 방식으로 저장합니다. UTF-16은 U+FFFF까지의 문자마다 2바이트를 주는데, U+00FF까지의 모든 것 — 영어 글자, 숫자, é, 그 밖의 Latin-1 — 은 상위 바이트가 00입니다. 하위 바이트가 먼저 오므로 그 0이 각 글자 뒤에 놓입니다. Hi는 48 00 69 00입니다.
SQL Server는 VARBINARY 값을 0x와 그 16진수로 보여 주므로, Hi를 담은 NVARCHAR 값을 VARBINARY로 캐스트하면 0x48006900으로 표시됩니다. UTF-8을 고른 채 그것을 여기에 붙여넣으면 텍스트는 H, NUL, i, NUL로 나오고, 그 아래의 알림이 라틴 문자 텍스트를 UTF-8이 아니라 UTF-16으로 썼을 때처럼 두 번째 바이트마다 00이라고 말하며, 그 옆에 ‘UTF-16 LE로 전환’이 있습니다. 그것을 누르면 같은 바이트가 Hi로 읽힙니다.
UTF-16 BE는 반대로 상위 바이트를 먼저 두므로, 거기서 Hi는 00 48 00 69이고, UTF-16 LE에서 D0 05인 א는 05 D0입니다. 각 쌍의 첫 자리에 0이 있으면 알림은 UTF-16 BE를 권합니다. U+00FF를 넘는 문자가 텍스트에 들어오면 알림은 아무 말도 하지 않는데, 그 문자의 상위 바이트가 00이 아니기 때문입니다. 알림은 두 번째 바이트마다 00인 곳에서만 말합니다.
맨 앞의 바이트 순서 표시
텍스트는 U+FEFF로 시작할 수 있습니다. 아무것도 보이지 않는 문자로, 바이트 순서 표시가 되려고 거기 있습니다. UTF-16에서는 그 두 바이트가 뒤따르는 모든 쌍의 순서를 알려 주며, UTF-16 LE는 FF FE, UTF-16 BE는 FE FF입니다. UTF-8에서는 EF BB BF 세 바이트로, 텍스트가 UTF-8임을 알리는 서명인데, UTF-8에는 순서가 하나뿐입니다.
페이지는 표시를 버리지 않고 남겨 둡니다. 고른 단위 자신의 표시로 시작하는 16진수는 U+FEFF로 시작하는 텍스트로 읽히고, 알림이 표시를 남겨 두었다고 말하므로, 그 텍스트를 다시 쓰면 표시까지 포함해 같은 바이트가 나옵니다. 다른 쪽 UTF-16 순서의 표시로 시작하는 16진수나, UTF-8을 골랐을 때 UTF-16 표시로 시작하는 16진수에는 바이트가 다른 단위의 표시로 시작한다는 알림이 붙고, 그 옆에 표시가 가리키는 순서로 바꾸는 버튼이 놓입니다. 맨 앞이 아닌 곳에서 U+FEFF는 평범한 문자이며 알림을 부르지 않습니다.
텍스트가 아닌 바이트는 U+FFFD로 표시
모든 바이트 시퀀스가 고른 단위에서 텍스트인 것은 아닙니다. 마지막 바이트가 빠진 문자, 오래된 코드 페이지가 é로 쓰는 E9 같은 떠돌이 바이트, UTF-16 끝에 홀로 남은 바이트는 모두 아무것도 나타내지 않습니다. 그래도 잘못된 시퀀스 하나가 붙여넣은 것 전체를 망치지는 않습니다. 그런 시퀀스는 저마다 대체 문자인 U+FFFD로 바뀌고, 나머지는 모두 평소대로 읽힙니다.
그러면 알림이 그 개수를 알려 주고, 첫 번째 것을 바이트 가운데 몇 번째인지, 여러분이 그것을 위해 쓴 숫자, 그리고 그 줄과 열로 가리킵니다. 서유럽 텍스트를 위한 Windows 코드 페이지인 Windows-1252로 저장한 Café는 43 61 66 E9이며, 거기서 é는 바이트 하나, E9입니다. 이것을 UTF-8로 읽으면 Caf와 U+FFFD로 돌아오고, 알림은 1번째 줄, 10번째 열에 E9로 쓴 4번째 바이트를 가리킵니다. UTF-8에서 이 단어는 43 61 66 C3 A9입니다.
알림이 세는 것은 바이트가 아니라 시퀀스입니다. 😀의 네 바이트 가운데 세 바이트인 F0 9F 98은 U+FFFD 하나입니다. 그리고 텍스트에 정말 들어 있는 U+FFFD, 곧 EF BF BD라는 바이트는 텍스트로 읽히고 아예 세지 않으므로, 알림은 언제나 읽히지 않은 바이트만 다룹니다.
16진수를 다시 파일로
되읽기는 텍스트뿐 아니라 바이트도 넘겨줍니다. 방향이 ‘16진수에서 텍스트로’이고 UTF-8, UTF-16 LE, UTF-16 BE 가운데 하나를 골랐다면, ‘내려받기’는 bytes.bin이라는 이름의 파일로, 읽은 바이트를 정확히 그대로 저장합니다. 텍스트가 아니었던 바이트도 포함하며, 무엇이든 대체되기 전의 바이트입니다. xxd -r이 16진수 덤프로 하는 일입니다. 바이트가 아닌 코드 포인트에는 ‘내려받기’가 없고, 가져갈 것이 숫자인 쓰기 방향에서도 없습니다.
그래서 한 번도 텍스트였던 적이 없는 것의 16진수도 온전히 돌아옵니다. 모든 PNG 이미지는 89 50 4E 47 0D 0A 1A 0A 여덟 바이트로 시작합니다. UTF-8로 읽으면 U+FFFD, PNG라는 글자, 제어 문자 네 개로 보이고, 텍스트가 아닌 시퀀스 하나에 대한 알림이 붙으며, ‘내려받기’는 여덟 바이트를 모두 정확히 저장합니다. 바이트에는 이름이 없으므로 파일 이름은 언제나 bytes.bin이니, 저장한 뒤 image.png처럼 제 이름을 붙여 주세요.
문자, 그 비트, 또는 수를 알고 싶을 때 갈 곳
문자가 무엇인지 — 이름, 범주, 그리고 보이지 않는 문자 가운데 하나인지 — 알려면, ‘유니코드 문자 검사기’가 텍스트를 코드 포인트 하나씩 분해하고 각각의 UTF-8 바이트도 함께 보여 줍니다.
‘텍스트 / 2진수 변환’은 바로 이 페이지를 2진수로 연 것으로, 거기서는 UTF-8이 따르는 패턴을 모든 바이트의 첫 비트들에서 읽을 수 있습니다. 그리고 수는 텍스트가 아닙니다. 여기서 입력한 255는 2, 5, 5라는 숫자이고 따라서 32 35 35라는 바이트이지만, ‘진법 변환기’는 255를 양으로 받아들여 16진수로 ff라고 씁니다.
자주 묻는 질문
- 16진수는 어떻게 텍스트로 바꾸나요?
- ‘16진수에서 텍스트로’를 고르고 16진수를 붙여넣은 다음, 단위를 그것이 쓰인 인코딩에 맞추세요. 대부분의 텍스트라면 UTF-8, Windows나 .NET 문자열 또는 NVARCHAR 열에서 가져온 16진수라면 UTF-16 LE입니다. 바이트 사이의 공백, 쉼표, 콜론, 하이픈, 바이트 앞의 0x나 \x, 전체를 한 번 감싼 것은 건너뛰고 읽으며, 대문자든 소문자든 마찬가지입니다.
- 왜 제 텍스트의 글자 사이마다 NUL이 끼어 있나요?
- 그 16진수는 아마 UTF-8로 읽은 UTF-16 LE일 것입니다. UTF-16 LE는 영어 글자마다 ASCII 값과 그 뒤의 00, 이렇게 두 바이트로 쓰고, UTF-8은 그 0 하나하나를 독립된 문자인 NUL로 읽습니다. UTF-16 LE를 고르거나, 페이지가 알림 옆에 전환 버튼을 내놓았다면 그것을 누르면 글자들이 다시 붙습니다.
- 제 16진수에서 악센트가 붙은 글자가 왜 U+FFFD로 나오나요?
- 아마 그 16진수는 Windows-1252 같은 오래된 코드 페이지로 쓰였을 것입니다. 거기서 é는 바이트 하나, E9이지만, UTF-8에서 é는 C3 A9이고 홀로 있는 E9는 텍스트가 아닙니다. 페이지는 UTF-8과 UTF-16을 읽고 오래된 코드 페이지는 읽지 않으므로, 어느 것을 뜻했는지 짐작하는 대신 그런 시퀀스를 저마다 U+FFFD로 보여 주고 첫 번째 것이 어디 있는지 알려 줍니다. 그래도 ‘내려받기’는 바이트를 있던 그대로 줍니다.
- xxd나 hexdump -C가 출력한 것을 붙여넣어도 되나요?
- 네. ‘16진수 덤프’ 스타일이 쓰는 것이기도 한 hexdump -C의 형식과, xxd가 옵션 없이 출력하는 형식을 붙여넣을 수 있습니다. 텍스트 열은 제쳐 두고 어떤 오프셋도 바이트로 읽지 않으며, * 줄은 다시 채워지고, 두 형식 가운데 어느 것으로 읽었는지 알림이 알려 줍니다. 앞의 바이트에서 이어지지 않는 오프셋은 그 줄에서 읽기를 멈추게 하는데, 줄들이 더는 서로 맞지 않기 때문입니다. xxd -p의 맨 16진수도 다른 16진수처럼 알림 없이 읽힙니다.
- 16진수가 대문자인지 소문자인지가 중요한가요?
- 바이트에는 상관없습니다. 4A와 4a는 같은 바이트, J입니다. 페이지는 ‘소문자’를 고르지 않는 한 대문자로 쓰고, 그 선택은 16진수 덤프의 바이트는 물론 오프셋에도 적용됩니다. 되읽기는 어느 쪽이든, 한 번 붙여넣은 것 안에서 섞여 있어도 받아들입니다.
- 16진수 덤프에서 파일을 되살리는 방법은 무엇인가요?
- ‘16진수에서 텍스트로’와 UTF-8 또는 두 UTF-16 가운데 하나를 고르고, 덤프나 맨 16진수를 붙여넣은 다음 ‘내려받기’를 누르세요. 저장되는 파일 bytes.bin에는 붙여넣은 것에서 읽은 바이트가 텍스트든 아니든 정확히 그대로 담기므로, xxd -r이 하는 일을 해 줍니다. 원래 이름으로 바꿔 주세요.
- 운영 환경의 데이터베이스나 로그에서 가져온 16진수를 붙여넣어도 안전한가요?
- 네, 변환에 관한 한 그렇습니다. 변환은 어느 방향으로 돌든 여러분 자신의 기기에서 일어나며, 붙여넣은 16진수, 그것이 바뀐 텍스트, ‘내려받기’가 저장하는 파일 어느 것도 어디로도 보내지지 않습니다.
관련 도구
- 텍스트 / 2진수 변환
2진수는 바이트를 8비트 그대로 쓰기 때문에 UTF-8의 패턴이 보입니다. é는 이 페이지에서 C3 A9로, 저 페이지에서 11000011 10101001로 쓰이는데, 글자가 2바이트라서 첫 바이트는 110으로 시작하고, 이어지는 바이트라서 둘째 바이트는 10으로 시작합니다. 이 페이지에도 2진수가 있지만, 저 페이지는 2진수로 열립니다.
- 유니코드 문자 검사기
문자열이 어떤 문자로 되어 있는지 정확히 보기.
- 진법 변환기
이 페이지에 255를 입력하면 문자마다 1바이트씩, 32 35 35의 3바이트가 나옵니다. 저 페이지는 255를 수로 읽어 값 자체를 변환하므로, 16진수로는 ff가 됩니다.
- Base64
Base64 인코딩과 디코딩 — UTF-8 완전 지원.