{ }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()・標準入力に渡されます(実行中に入力を求めません)。

$ 準備完了

コードを入力して解くにはパソコンで開いてください。モバイルでは問題と解答を見られます。

ほかの言語では?言語別の解答を見る · 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 のようにマニピュレータをかけます。
Craykon には 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);