{ }codelper
모양 맞춰 출력P0396

바이트 덤프 표 찍기

파일에 든 바이트를 눈으로 살펴보려고 덤프 표를 찍는다. 첫 줄에 바이트의 수 n, 한 줄에 놓는 바이트의 수 w, 첫 바이트의 주소 s가 주어진다. 둘째 줄에 바이트 n개가 차례로 주어진다. 바이트를 앞에서부터 w개씩 묶어 한 줄씩 쓴다.
마지막 줄은 w개보다 적을 수 있다. 0부터 세어 k번째 바이트의 주소는 s + k이다. 한 줄은 다음 네 부분을 차례로 이어 쓴다. 첫째, 그 줄 첫 바이트의 주소를 16진수 소문자 여덟 자리로 쓰고 쌍점(:)을 붙인다.
자리가 모자라면 앞을 0으로 채운다. 둘째, 바이트마다 빈칸 하나를 쓰고 그 바이트를 16진수 대문자 두 자리로 쓴다(10은 0A). 셋째, 그 줄의 바이트가 w개보다 적으면 모자란 바이트 하나마다 빈칸 세 개를 쓴다. 넷째, 빈칸 두 개와 | 를 쓰고, 바이트마다 글자 하나를 쓴 뒤 | 로 닫는다.
바이트의 값이 32 이상 126 이하이면 그 코드의 ASCII 글자를 쓰고, 아니면 마침표(.)를 쓴다. 바이트의 개수와 값이 바로 앞 묶음과 모두 같은 묶음은 줄을 쓰지 않는다. 이렇게 쓰지 않은 줄이 잇달아 있으면, 몇 줄이든 그 자리에 * 하나만 있는 줄을 한 번 쓴다. 맨 끝에 n bytes, sum S 꼴의 줄을 쓴다.
n과 S는 10진수이고, S는 모든 바이트의 값을 더한 수다.

입력
1 ≤ n ≤ 6000, 1 ≤ w ≤ 16, 0 ≤ s, s + n ≤ 4294967296.
바이트는 0 이상 255 이하의 정수이고 빈칸 하나로 띄워 주어진다
출력
덤프 줄들(쓰지 않은 줄의 자리에는 *)과 맨 끝의 n bytes, sum S 줄
예시 1 · stdin
19 8 65528
72 101 108 108 111 44 32 119 111 114 108 100 33 10 0 127 255 9 7
stdout
0000fff8: 48 65 6C 6C 6F 2C 20 77  |Hello, w|
00010000: 6F 72 6C 64 21 0A 00 7F  |orld!...|
00010008: FF 09 07                 |...|
19 bytes, sum 1569
예시 2 · stdin
14 4 0
0 0 0 0 0 0 0 0 0 0 0 0 65 66
stdout
00000000: 00 00 00 00  |....|
*
0000000c: 41 42        |AB|
14 bytes, sum 131
main.py
입력(stdin)

실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.

다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이

수를 16진수로, 자리 수까지 맞춰 쓰는 방법이 언어마다 달라요.
Python은 f"{b:02X}"와 f"{a:08x}", C는 printf("%02X", b)와 printf("%08llx", a), Java는 String.format("%02X", b)와 String.format("%08x", a), C#은 b.ToString("X2")와 a.ToString("x8")처럼 서식 하나로 진법·자리 수·대소문자를 함께 정해요.
JS·TS의 b.toString(16)은 소문자이고 자리도 맞춰 주지 않아서 toUpperCase()와 padStart(2, "0")를 이어 써요.
C++은 std::cout << std::hex << std::setfill('0') << std::setw(2) << b처럼 조작자를 걸어요.
크레이콘에는 16진수 서식이 없어서 남은 % 16으로 한 자리를 고르고 수학.몫(남은, 16)으로 줄여 가며 직접 만들고, 글자는 글자.글자로변환(b)로 얻어요.
함정 하나: 0 채우기를 빼면(Python {b:X}) 16보다 작은 바이트가 한 자리가 돼요.
첫째 예시의 10이 0A가 아니라 A로 나오고 그 뒤가 한 칸씩 당겨져요.
함정 둘: JS의 toString(16)은 소문자라서 toUpperCase()를 빼면 6C가 6c로 나와요.
주소는 소문자, 바이트는 대문자예요.
함정 셋: C++의 std::hex, std::setfill, std::uppercase는 한 번 걸면 계속 남아요(std::setw만 바로 다음 값 하나에 걸려요).
std::dec으로 되돌리지 않으면 첫째 예시의 맨 끝 줄이 13 bytes, sum 621이 돼요.
함정 넷: 마지막 줄이 짧을 때 모자란 바이트마다 빈칸 세 개를 채우지 않으면 |가 앞으로 당겨져요(첫째 예시의 셋째 줄).
함정 다섯: 글자로 쓰는 것은 126까지예요.
127(DEL)은 보이는 글자가 아니에요.
b <= 127로 쓰면 첫째 예시 둘째 줄에 . 대신 127번 글자가 그대로 들어가요.
함정 여섯: 같은 묶음이 여러 번 이어져도 *는 한 번만 써요.
둘째 예시에서 쓰지 않은 줄은 둘이지만 *는 한 줄이에요.
함정 일곱: 주소가 4294967295까지라서 32비트 int(약 21억까지)에 담기지 않아요.
C#에서 주소를 int.Parse로 읽으면 OverflowException으로 멈춰요.
C·C++은 long long, Java·C#은 long에 담아요.
이 경우는 예시에 없어요(s가 2147483648 이상일 때 드러나요).

Pythonmain.py · 22 줄
n, w, s = map(int, input().split())data = list(map(int, input().split())) out = []prev = None        # 바로 앞 줄의 바이트들starred = False    # 생략 표시 *를 이미 썼는가for i in range(0, n, w):    row = data[i:i + w]    if row == prev:        if not starred:            out.append("*")            starred = True        continue    prev = row    starred = False    # 바이트는 대문자 16진수 두 자리, 모자란 칸은 빈칸 세 개    hexes = "".join(f" {b:02X}" for b in row) + "   " * (w - len(row))    text = "".join(chr(b) if 32 <= b <= 126 else "." for b in row)    # 주소는 소문자 16진수 여덟 자리    out.append(f"{s + i:08x}:{hexes}  |{text}|")out.append(f"{n} bytes, sum {sum(data)}")print("\n".join(out))
JavaScriptmain.js · 26 줄
const lines = require("fs").readFileSync(0, "utf8").split("\n");const [n, w, s] = lines[0].trim().split(/\s+/).map(Number);const data = lines[1].trim().split(/\s+/).map(Number); const out = [];let prev = "";       // 바로 앞 줄의 바이트 부분(주소 뺀 문자열)let starred = false; // 생략 표시 *를 이미 썼는가let sum = 0;for (let i = 0; i < n; i += w) {  const row = data.slice(i, i + w);  for (const b of row) sum += b;  // 바이트는 대문자 16진수 두 자리, 모자란 칸은 빈칸 세 개  const hexes = row.map((b) => " " + b.toString(16).toUpperCase().padStart(2, "0")).join("") + "   ".repeat(w - row.length);  if (hexes === prev) {    if (!starred) out.push("*");    starred = true;    continue;  }  prev = hexes;  starred = false;  const text = row.map((b) => (b >= 32 && b <= 126 ? String.fromCharCode(b) : ".")).join("");  // 주소는 소문자 16진수 여덟 자리  out.push(`${(s + i).toString(16).padStart(8, "0")}:${hexes}  |${text}|`);}out.push(`${n} bytes, sum ${sum}`);console.log(out.join("\n"));
TypeScriptmain.ts · 29 줄
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const [n, w, s]: number[] = lines[0].trim().split(/\s+/).map(Number);const data: number[] = lines[1].trim().split(/\s+/).map(Number); const hex = (value: number, width: number): string => value.toString(16).padStart(width, "0"); const out: string[] = [];let prev = "";       // 바로 앞 줄의 바이트 부분(주소 뺀 문자열)let starred = false; // 생략 표시 *를 이미 썼는가let sum = 0;for (let i = 0; i < n; i += w) {  const row: number[] = data.slice(i, i + w);  for (const b of row) sum += b;  // 바이트는 대문자 16진수 두 자리, 모자란 칸은 빈칸 세 개  const hexes: string = row.map((b) => " " + hex(b, 2).toUpperCase()).join("") + "   ".repeat(w - row.length);  if (hexes === prev) {    if (!starred) out.push("*");    starred = true;    continue;  }  prev = hexes;  starred = false;  const text: string = row.map((b) => (b >= 32 && b <= 126 ? String.fromCharCode(b) : ".")).join("");  // 주소는 소문자 16진수 여덟 자리  out.push(`${hex(s + i, 8)}:${hexes}  |${text}|`);}out.push(`${n} bytes, sum ${sum}`);console.log(out.join("\n"));
JavaMain.java · 45 줄
import java.io.*;import java.util.*; public class Main {    public static void main(String[] args) throws IOException {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        StringTokenizer st = new StringTokenizer(in.readLine());        int n = Integer.parseInt(st.nextToken());        int w = Integer.parseInt(st.nextToken());        long s = Long.parseLong(st.nextToken());   // 주소는 2^31을 넘을 수 있어 long        st = new StringTokenizer(in.readLine());        int[] data = new int[n];        long sum = 0;        for (int i = 0; i < n; i++) {            data[i] = Integer.parseInt(st.nextToken());            sum += data[i];        }         StringBuilder out = new StringBuilder();        boolean starred = false;   // 생략 표시 *를 이미 썼는가        for (int i = 0; i < n; i += w) {            int len = Math.min(w, n - i);            // 바로 앞 줄(i - w부터 w개)과 바이트가 모두 같은가            if (i > 0 && len == w && Arrays.equals(data, i, i + w, data, i - w, i)) {                if (!starred) out.append("*\n");                starred = true;                continue;            }            starred = false;            out.append(String.format("%08x:", s + i));          // 소문자 16진수 여덟 자리            for (int j = 0; j < w; j++) {                if (j < len) out.append(String.format(" %02X", data[i + j]));   // 대문자 두 자리                else out.append("   ");            }            out.append("  |");            for (int j = 0; j < len; j++) {                int b = data[i + j];                out.append(b >= 32 && b <= 126 ? (char) b : '.');            }            out.append("|\n");        }        out.append(n).append(" bytes, sum ").append(sum).append('\n');        System.out.print(out);    }}
C#Main.cs · 43 줄
using System;using System.Text; class Program{    static void Main()    {        string[] head = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries);        int n = int.Parse(head[0]);        int w = int.Parse(head[1]);        long s = long.Parse(head[2]);   // 주소는 2^31을 넘을 수 있어 long        int[] data = Array.ConvertAll(Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries), int.Parse);         var output = new StringBuilder();        bool starred = false;   // 생략 표시 *를 이미 썼는가        long sum = 0;        for (int i = 0; i < n; i += w)        {            int len = Math.Min(w, n - i);            var row = new ReadOnlySpan<int>(data, i, len);            foreach (int b in row) sum += b;            // 바로 앞 줄(i - w부터 w개)과 바이트가 모두 같은가            if (i > 0 && row.SequenceEqual(new ReadOnlySpan<int>(data, i - w, w)))            {                if (!starred) output.Append("*\n");                starred = true;                continue;            }            starred = false;            output.Append((s + i).ToString("x8")).Append(':');   // 소문자 16진수 여덟 자리            for (int j = 0; j < w; j++)            {                if (j < len) output.Append(' ').Append(data[i + j].ToString("X2"));   // 대문자 두 자리                else output.Append("   ");            }            output.Append("  |");            foreach (int b in row) output.Append(b >= 32 && b <= 126 ? (char)b : '.');            output.Append("|\n");        }        output.Append($"{n} bytes, sum {sum}\n");        Console.Write(output);    }}
C++main.cpp · 45 줄
#include <algorithm>#include <iomanip>#include <iostream>#include <vector> int main() {    std::ios::sync_with_stdio(false);    std::cin.tie(nullptr);    int n, w;    long long s;   // 주소는 2^31을 넘을 수 있어 long long    std::cin >> n >> w >> s;    std::vector<int> data(n);    long long sum = 0;    for (int& b : data) {        std::cin >> b;        sum += b;    }     bool starred = false;   // 생략 표시 *를 이미 썼는가    for (int i = 0; i < n; i += w) {        int len = std::min(w, n - i);        // 바로 앞 줄(i - w부터 w개)과 바이트가 모두 같은가        if (i > 0 && len == w && std::equal(data.begin() + i, data.begin() + i + w, data.begin() + i - w)) {            if (!starred) std::cout << "*\n";            starred = true;            continue;        }        starred = false;        // hex, setfill, uppercase는 한 번 걸면 계속 남고, setw는 바로 다음 값 하나에만 걸린다        std::cout << std::hex << std::setfill('0');        std::cout << std::nouppercase << std::setw(8) << s + i << ':';        std::cout << std::uppercase;        for (int j = 0; j < w; j++) {            if (j < len) std::cout << ' ' << std::setw(2) << data[i + j];            else std::cout << "   ";        }        std::cout << "  |";        for (int j = 0; j < len; j++) {            int b = data[i + j];            std::cout << (b >= 32 && b <= 126 ? static_cast<char>(b) : '.');        }        std::cout << "|\n";    }    std::cout << std::dec << n << " bytes, sum " << sum << '\n';   // 10진수로 되돌린다}
Cmain.c · 42 줄
#include <stdio.h>#include <string.h> static unsigned char data[6000]; int main(void) {    int n, w;    long long s;   /* 주소는 2^31을 넘을 수 있어 long long */    scanf("%d %d %lld", &n, &w, &s);    long long sum = 0;    for (int i = 0; i < n; i++) {        int b;        scanf("%d", &b);        data[i] = (unsigned char)b;        sum += b;    }     int starred = 0;   /* 생략 표시 *를 이미 썼는가 */    for (int i = 0; i < n; i += w) {        int len = n - i < w ? n - i : w;        /* 바로 앞 줄(i - w부터 w개)과 바이트가 모두 같은가 */        if (i > 0 && len == w && memcmp(data + i, data + i - w, (size_t)w) == 0) {            if (!starred) puts("*");            starred = 1;            continue;        }        starred = 0;        printf("%08llx:", (unsigned long long)(s + i));   /* 소문자 16진수 여덟 자리 */        for (int j = 0; j < w; j++) {            if (j < len) printf(" %02X", data[i + j]);     /* 대문자 두 자리 */            else printf("   ");        }        printf("  |");        for (int j = 0; j < len; j++) {            unsigned char b = data[i + j];            putchar(b >= 32 && b <= 126 ? b : '.');        }        puts("|");    }    printf("%d bytes, sum %lld\n", n, sum);    return 0;}
Craykonmain.cray · 65 줄
// 수를 16진수 문자열로 바꾼다(자리 수만큼 앞을 0으로 채움)십육진수(값, 자리, 숫자들){    결과 = "";    남은 = 값;    반복 (k = 0; k < 자리; k++)    {        결과 = 숫자들[남은 % 16] + 결과;        남은 = 수학.몫(남은, 16);    }    돌려주기 결과;} 첫줄 = 콘솔.수들읽기();n = 첫줄[0];w = 첫줄[1];s = 첫줄[2];data = 콘솔.수들읽기(); 이전 = ""; // 바로 앞 줄의 바이트 부분(주소 뺀 문자열)별표씀 = 거짓; // 생략 표시 *를 이미 썼는가합 = 0;out = "";반복 (i = 0; i < n; i += w){    바이트들 = "";    글자들 = "";    반복 (j = 0; j < w; j++)    {        만약 (i + j < n)        {            b = data[i + j];            합 += b;            바이트들 = 바이트들 + " " + 십육진수(b, 2, "0123456789ABCDEF");            만약 (b >= 32 && b <= 126)            {                글자들 = 글자들 + 글자.글자로변환(b);            }            아니면            {                글자들 = 글자들 + ".";            }        }        아니면        {            바이트들 = 바이트들 + "   ";        }    }    만약 (바이트들 == 이전)    {        만약 (!별표씀)        {            out = out + "*\n";        }        별표씀 = 참;    }    아니면    {        이전 = 바이트들;        별표씀 = 거짓;        out = out + 십육진수(s + i, 8, "0123456789abcdef") + ":" + 바이트들 + "  |" + 글자들 + "|\n";    }}out = out + n + " bytes, sum " + 합 + "\n";콘솔.이어쓰기(out);