INFO 7
08:00:01 DEBUG boot sequence start
08:00:02 INFO disk ok
08:00:03 WARN fan slow
08:00:04 DEBUG fan check
08:00:05 WARN fan slow
08:00:09 WARN fan slow
08:00:10 ERROR disk ok
stdout
08:00:02 [INFO] disk ok
08:00:03 [WARN] fan slow (x2)
08:00:09 [WARN] fan slow
08:00:10 [ERROR] disk ok
DEBUG=2, INFO=1, WARN=3, ERROR=1
import sys# 차례가 곧 단계의 높이다. 튜플이라 칸을 바꿀 수 없다LEVELS = ("DEBUG", "INFO", "WARN", "ERROR")min_name, n = sys.stdin.readline().split()min_rank = LEVELS.index(min_name)counts = [0] * len(LEVELS)runs = [] # [시각, 단계, 메시지, 개수]for _ in range(int(n)): line = sys.stdin.readline().rstrip("\n") # 앞의 두 빈칸에서만 나눈다: 메시지 안의 빈칸(두 칸 이상도)은 그대로 남는다 time, level, message = line.split(" ", 2) rank = LEVELS.index(level) counts[rank] += 1 if rank < min_rank: continue # 걸러진 줄은 묶음을 끊지 않는다 if runs and runs[-1][1] == level and runs[-1][2] == message: runs[-1][3] += 1 else: runs.append([time, level, message, 1])for time, level, message, k in runs: print(f"{time} [{level}] {message}" + (f" (x{k})" if k > 1 else ""))print(", ".join(f"{name}={c}" for name, c in zip(LEVELS, counts)))
JavaScriptmain.js · 26 行
// const 배열: 이름은 다시 넣을 수 없고, Object.freeze로 칸도 못 바꾸게 한다const LEVELS = Object.freeze(["DEBUG", "INFO", "WARN", "ERROR"]);const lines = require("fs").readFileSync(0, "utf8").split("\n");const [minName, n] = lines[0].trim().split(/\s+/);const minRank = LEVELS.indexOf(minName);const counts = [0, 0, 0, 0];const runs = []; // { time, level, message, k }for (let i = 1; i <= Number(n); i++) { const line = lines[i]; // 앞의 두 빈칸 자리로 자른다: split(" ")은 메시지 안의 빈칸까지 나눈다 const a = line.indexOf(" "); const b = line.indexOf(" ", a + 1); const time = line.slice(0, a); const level = line.slice(a + 1, b); const message = line.slice(b + 1); const rank = LEVELS.indexOf(level); counts[rank]++; if (rank < minRank) continue; // 걸러진 줄은 묶음을 끊지 않는다 const last = runs[runs.length - 1]; if (last && last.level === level && last.message === message) last.k++; else runs.push({ time, level, message, k: 1 });}const out = runs.map((r) => `${r.time} [${r.level}] ${r.message}` + (r.k > 1 ? ` (x${r.k})` : ""));out.push(LEVELS.map((name, i) => `${name}=${counts[i]}`).join(", "));console.log(out.join("\n"));
TypeScriptmain.ts · 29 行
import { readFileSync } from "node:fs";// readonly 배열: 타입 검사기가 칸을 바꾸지 못하게 막는다const LEVELS: readonly string[] = ["DEBUG", "INFO", "WARN", "ERROR"];interface Run { time: string; level: string; message: string; k: number }const lines: string[] = readFileSync(0, "utf8").split("\n");const [minName, n] = lines[0].trim().split(/\s+/);const minRank: number = LEVELS.indexOf(minName);const counts: number[] = [0, 0, 0, 0];const runs: Run[] = [];for (let i = 1; i <= Number(n); i++) { const line: string = lines[i]; // 앞의 두 빈칸 자리로 자른다: 메시지 안의 빈칸은 그대로 const a: number = line.indexOf(" "); const b: number = line.indexOf(" ", a + 1); const time = line.slice(0, a); const level = line.slice(a + 1, b); const message = line.slice(b + 1); const rank: number = LEVELS.indexOf(level); counts[rank]++; if (rank < minRank) continue; // 걸러진 줄은 묶음을 끊지 않는다 const last: Run | undefined = runs[runs.length - 1]; if (last !== undefined && last.level === level && last.message === message) last.k++; else runs.push({ time, level, message, k: 1 });}const out: string[] = runs.map((r) => `${r.time} [${r.level}] ${r.message}` + (r.k > 1 ? ` (x${r.k})` : ""));out.push(LEVELS.map((name, i) => `${name}=${counts[i]}`).join(", "));console.log(out.join("\n"));
JavaMain.java · 46 行
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.util.ArrayList;import java.util.List;import java.util.StringJoiner;public class Main { // final은 이름만 묶는다: LEVELS[0] = "X"는 막지 않으니 List.of로 바꿀 수 없는 목록을 쓴다 static final List<String> LEVELS = List.of("DEBUG", "INFO", "WARN", "ERROR"); public static void main(String[] args) throws IOException { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); String[] first = in.readLine().trim().split("\\s+"); int minRank = LEVELS.indexOf(first[0]); int n = Integer.parseInt(first[1]); int[] counts = new int[LEVELS.size()]; List<String[]> runs = new ArrayList<>(); // {시각, 단계, 메시지} List<Integer> sizes = new ArrayList<>(); for (int i = 0; i < n; i++) { // split(" ", 3): 앞의 두 빈칸에서만 나눠 메시지 안의 빈칸은 그대로 남는다 String[] p = in.readLine().split(" ", 3); int rank = LEVELS.indexOf(p[1]); counts[rank]++; if (rank < minRank) continue; // 걸러진 줄은 묶음을 끊지 않는다 int last = runs.size() - 1; if (last >= 0 && runs.get(last)[1].equals(p[1]) && runs.get(last)[2].equals(p[2])) sizes.set(last, sizes.get(last) + 1); else { runs.add(p); sizes.add(1); } } StringBuilder out = new StringBuilder(); for (int i = 0; i < runs.size(); i++) { String[] r = runs.get(i); out.append(r[0]).append(" [").append(r[1]).append("] ").append(r[2]); if (sizes.get(i) > 1) out.append(" (x").append(sizes.get(i)).append(')'); out.append('\n'); } StringJoiner sj = new StringJoiner(", "); for (int i = 0; i < LEVELS.size(); i++) sj.add(LEVELS.get(i) + "=" + counts[i]); out.append(sj).append('\n'); System.out.print(out); }}
C#Main.cs · 37 行
using System;using System.Collections.Generic;using System.Linq;using System.Text;class Program{ // const는 배열이 될 수 없다: static readonly는 이름만 묶고, 칸은 바꿀 수 있다 static readonly string[] Levels = { "DEBUG", "INFO", "WARN", "ERROR" }; static void Main() { string[] first = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries); int minRank = Array.IndexOf(Levels, first[0]); int n = int.Parse(first[1]); int[] counts = new int[Levels.Length]; var runs = new List<(string Time, string Level, string Message, int K)>(); for (int i = 0; i < n; i++) { // Split(' ', 3): 앞의 두 빈칸에서만 나눠 메시지 안의 빈칸은 그대로 string[] p = Console.ReadLine().Split(' ', 3); int rank = Array.IndexOf(Levels, p[1]); counts[rank]++; if (rank < minRank) continue; // 걸러진 줄은 묶음을 끊지 않는다 int last = runs.Count - 1; if (last >= 0 && runs[last].Level == p[1] && runs[last].Message == p[2]) runs[last] = runs[last] with { K = runs[last].K + 1 }; else runs.Add((p[0], p[1], p[2], 1)); } var sb = new StringBuilder(); foreach (var r in runs) sb.Append($"{r.Time} [{r.Level}] {r.Message}").Append(r.K > 1 ? $" (x{r.K})" : "").Append('\n'); sb.Append(string.Join(", ", Levels.Select((name, i) => $"{name}={counts[i]}"))).Append('\n'); Console.Write(sb); }}
C++main.cpp · 55 行
#include <algorithm>#include <array>#include <iostream>#include <sstream>#include <string>#include <string_view>#include <vector>// constexpr 배열: 컴파일할 때 정해지고 칸도 바꿀 수 없다constexpr std::array<std::string_view, 4> LEVELS{"DEBUG", "INFO", "WARN", "ERROR"};int rankOf(const std::string& name) { return static_cast<int>(std::find(LEVELS.begin(), LEVELS.end(), name) - LEVELS.begin());}struct Run { std::string time, level, message; int k;};int main() { std::string line; std::getline(std::cin, line); std::istringstream head(line); std::string minName; int n; head >> minName >> n; int minRank = rankOf(minName); std::array<int, 4> counts{}; std::vector<Run> runs; for (int i = 0; i < n; i++) { std::getline(std::cin, line); // 빈칸이 든 한 줄을 통째로 // 앞의 두 빈칸 자리로 자른다: 메시지 안의 빈칸은 그대로 std::size_t a = line.find(' '), b = line.find(' ', a + 1); std::string time = line.substr(0, a), level = line.substr(a + 1, b - a - 1), message = line.substr(b + 1); int rank = rankOf(level); counts[rank]++; if (rank < minRank) continue; // 걸러진 줄은 묶음을 끊지 않는다 if (!runs.empty() && runs.back().level == level && runs.back().message == message) runs.back().k++; else runs.push_back({time, level, message, 1}); } std::string out; for (const Run& r : runs) { out += r.time + " [" + r.level + "] " + r.message; if (r.k > 1) out += " (x" + std::to_string(r.k) + ")"; out += '\n'; } for (std::size_t i = 0; i < LEVELS.size(); i++) { if (i > 0) out += ", "; out += std::string(LEVELS[i]) + "=" + std::to_string(counts[i]); } std::cout << out << '\n';}
Cmain.c · 55 行
#include <stdio.h>#include <string.h>#define LEVEL_COUNT 4static const char *const LEVELS[LEVEL_COUNT] = {"DEBUG", "INFO", "WARN", "ERROR"};static int rank_of(const char *name) { for (int i = 0; i < LEVEL_COUNT; i++) if (strcmp(LEVELS[i], name) == 0) return i; return -1;}int main(void) { char line[128], min_name[8]; int n; fgets(line, sizeof line, stdin); sscanf(line, "%7s %d", min_name, &n); int min_rank = rank_of(min_name); int counts[LEVEL_COUNT] = {0}; char run_time[9] = "", run_msg[64] = ""; int run_rank = -1, run_k = 0; for (int i = 0; i < n; i++) { fgets(line, sizeof line, stdin); /* 빈칸이 든 한 줄을 통째로 */ line[strcspn(line, "\n")] = '\0'; /* 앞의 두 빈칸을 '\0'으로 바꿔 세 조각으로: 메시지 안의 빈칸은 그대로 */ char *level = strchr(line, ' '); *level++ = '\0'; char *msg = strchr(level, ' '); *msg++ = '\0'; int rank = rank_of(level); counts[rank]++; if (rank < min_rank) continue; /* 걸러진 줄은 묶음을 끊지 않는다 */ if (run_k > 0 && rank == run_rank && strcmp(msg, run_msg) == 0) { run_k++; continue; } if (run_k > 0) { printf("%s [%s] %s", run_time, LEVELS[run_rank], run_msg); if (run_k > 1) printf(" (x%d)", run_k); putchar('\n'); } strcpy(run_time, line); strcpy(run_msg, msg); run_rank = rank; run_k = 1; } if (run_k > 0) { printf("%s [%s] %s", run_time, LEVELS[run_rank], run_msg); if (run_k > 1) printf(" (x%d)", run_k); putchar('\n'); } for (int i = 0; i < LEVEL_COUNT; i++) printf("%s%s=%d", i ? ", " : "", LEVELS[i], counts[i]); putchar('\n'); return 0;}