설정 파일의 줄을 읽어 이름과 값을 다듬는다.
첫 줄에 줄 수 n이 주어지고, 이어서 n개의 줄이 주어진다.
각 줄을 다음 순서로 처리한다.
먼저 줄의 양 끝에서 빈칸과 탭을 모두 뗀다.
그 결과가 빈 문자열이거나 '#'으로 시작하면 설정이 아니므로 건너뛴다.
나머지 줄은 처음 나오는 '='에서 둘로 나눈다.
'='의 왼쪽에서 양 끝의 빈칸과 탭을 뗀 것이 이름이다.
'='의 오른쪽은 값이고, 그 안에 '='이나 '#'이 더 있어도 값의 일부다.
값은 양 끝의 빈칸과 탭을 뗀 다음, 끝에서부터 빈칸, 탭, 쉼표(','), 세미콜론(';')이 이어지는 동안 이 네 글자를 모두 뗀다.
쉼표와 세미콜론은 이렇게 끝에서만 떼고 값의 앞쪽에서는 떼지 않지만, 남은 글자가 모두 이 네 글자이면 전부 떼어져 값이 빈 문자열이 된다.
그런 다음 값의 길이가 2 이상이고 첫 글자와 끝 글자가 같은 따옴표이면(둘 다 큰따옴표이거나 둘 다 작은따옴표) 그 한 쌍만 벗긴다.
따옴표를 벗긴 뒤에는 더 다듬지 않으므로, 따옴표 안쪽의 빈칸과 쉼표와 또 다른 따옴표는 그대로 남는다.
짝이 맞지 않는 따옴표와 하나뿐인 따옴표는 그대로 둔다.
설정마다 입력에 나온 차례대로 한 줄에 "이름=[값]" 꼴로 출력하라.
값이 빈 문자열이면 "이름=[]"이 된다.
마지막 줄에는 설정의 수를 출력하라.
입력
첫 줄에 n(1 ≤ n ≤ 1,000). 이어서 n줄, 각 줄은 200글자 이하이고 빈 줄일 수 있으며, 탭과 출력 가능한 ASCII 글자(빈칸 포함)로 이루어진다. 건너뛰지 않는 줄에는 '='이 하나 이상 있고, 이름은 영문자·숫자·밑줄 1~20글자다
출력
설정마다 "이름=[값]" 한 줄, 마지막 줄에 설정의 수
예시 1 · stdin
10
# mode=test
name = " Kim " ,
url=http://a.b/?q=1;
tags = ,a, b,, ;
quote=""hi""
mark = "
mix='x"
port=8080
empty =
실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).
$ 준비됐어요
코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.
다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이
다듬는 도구가 언어마다 뗄 글자와 방향을 정하는 법이 달라요. Python은 strip(" \t"), lstrip, rstrip에 뗄 글자들을 문자열로 넘기고, C#은 Trim(' ', '\t'), TrimStart, TrimEnd에 글자를 나열해요. 그래서 값 끝의 네 글자는 Python rstrip(" \t,;"), C# TrimEnd(' ', '\t', ',', ';') 한 번이면 돼요. JS·TS trim(), Java strip(), 크레이콘 공백제거()는 뗄 글자를 고를 수 없어서, 쉼표와 세미콜론은 끝에서부터 글자를 하나씩 보며 끝 위치를 줄여요. 이 셋은 빈칸과 탭 말고 다른 공백 문자도 떼지만, 이 문제의 줄에는 공백 문자가 빈칸과 탭뿐이라 결과가 같아요. 그래도 정답은 JS·TS에서 정규식 /^[ \t]+/와 /[ \t]+$/로, Java에서 직접 만든 함수로 빈칸과 탭만 떼요. C에는 다듬는 함수가 없어서 포인터 두 개를 안쪽으로 옮기고, C++는 find_first_not_of와 find_last_not_of로 자리를 찾아 substr로 잘라요. 함정 하나: C++ find_first_not_of는 남는 글자가 없으면 npos를 돌려줘요. 그대로 substr에 넘기면 예시의 빈 줄에서 std::out_of_range로 멈춰요. 함정 둘: 줄을 다듬기 전에 '#'을 확인하면 예시 첫 줄 # mode=test를 설정으로 읽어 # mode=[test]를 출력해요. 함정 셋: '='은 첫 것에서만 나눠요. Python split("=", 1), Java split("=", 2), C# Split('=', 2)는 나머지를 마지막 조각에 그대로 두지만, JS·TS split("=", 2)는 조각을 2개만 남기고 나머지를 버려서 예시의 url이 http://a.b/?q가 돼요. indexOf("=")(크레이콘 자리찾기("="))로 자리를 찾아 잘라요. 함정 넷: 값 끝의 네 글자는 한꺼번에 떼야 해요. rstrip(",;") 다음에 rstrip(" \t")처럼 두 번에 나누면 예시의 tags가 ,a, b,,가 돼요. 함정 다섯: 쉼표와 세미콜론은 끝에서만 떼요. C# Trim(' ', '\t', ',', ';')처럼 양 끝에서 떼면 tags의 맨 앞 쉼표가 사라져 a, b가 돼요. 함정 여섯: 쉼표를 떼는 일이 따옴표보다 먼저예요. 순서를 바꾸면 예시의 name은 끝 글자가 쉼표라서 따옴표가 벗겨지지 않고 " Kim "이 돼요. 함정 일곱: Python strip('"')와 C# Trim('"')은 한 쌍이 아니라 양 끝의 따옴표를 모두 떼요. 예시의 quote가 "hi"가 아니라 hi가 돼요. 한 쌍은 잘라서 벗겨요. Python value[1:-1], JS·TS slice(1, -1), Java substring(1, len - 1)은 끝 위치를 주고, C# Substring(1, len - 2), C++ substr(1, len - 2), 크레이콘 부분문자열(1, len - 2)는 길이를 줘요. 함정 여덟: 길이가 2 이상인지 봐야 해요. 따옴표 하나뿐인 값은 첫 글자와 끝 글자가 같은 글자예요. 확인하지 않으면 예시의 mark가 Python에서는 mark=[]가 되고, Java에서는 substring(1, 0)이 StringIndexOutOfBoundsException으로 멈춰요. 함정 아홉: JS 정규식 /^["']|["']$/g로 앞뒤 따옴표를 따로 지우면 짝이 맞지 않는 예시의 mix가 x가 돼요. 함정 열: 따옴표를 벗긴 뒤에 다시 다듬으면 name이 Kim 이 아니라 Kim이 돼요.
Pythonmain.py · 20 줄
import syslines = sys.stdin.read().split("\n")n = int(lines[0])out = []count = 0for line in lines[1:n + 1]: line = line.strip(" \t") # 먼저 줄의 양 끝을 다듬는다 if line == "" or line[0] == "#": # 빈 줄과 주석은 건너뛴다 continue eq = line.index("=") # 첫 '='에서만 나눈다 key = line[:eq].rstrip(" \t") value = line[eq + 1:].lstrip(" \t") value = value.rstrip(" \t,;") # 끝에서만, 네 글자 중 무엇이든 한꺼번에 뗀다 if len(value) >= 2 and value[0] == value[-1] and value[0] in "\"'": value = value[1:-1] # 같은 따옴표 한 쌍만 벗긴다 out.append(f"{key}=[{value}]") count += 1out.append(str(count))print("\n".join(out))
JavaScriptmain.js · 22 줄
const lines = require("fs").readFileSync(0, "utf8").split("\n");const n = Number(lines[0]);const out = [];let count = 0;for (let i = 1; i <= n; i++) { // trim()은 다른 공백 문자까지 떼므로 빈칸과 탭만 정규식으로 뗀다 const line = lines[i].replace(/^[ \t]+/, "").replace(/[ \t]+$/, ""); if (line === "" || line[0] === "#") continue; const eq = line.indexOf("="); // 첫 '='에서만 나눈다 const key = line.slice(0, eq).replace(/[ \t]+$/, ""); let value = line.slice(eq + 1).replace(/^[ \t]+/, ""); let end = value.length; // 끝에서만, 네 글자 중 무엇이든 한꺼번에 뗀다 while (end > 0 && " \t,;".includes(value[end - 1])) end--; value = value.slice(0, end); if (value.length >= 2 && value[0] === value[value.length - 1] && (value[0] === '"' || value[0] === "'")) { value = value.slice(1, -1); // 같은 따옴표 한 쌍만 벗긴다 } out.push(`${key}=[${value}]`); count++;}out.push(String(count));console.log(out.join("\n"));
TypeScriptmain.ts · 24 줄
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const n = Number(lines[0]);const out: string[] = [];let count = 0;for (let i = 1; i <= n; i++) { // trim()은 다른 공백 문자까지 떼므로 빈칸과 탭만 정규식으로 뗀다 const line = lines[i].replace(/^[ \t]+/, "").replace(/[ \t]+$/, ""); if (line === "" || line[0] === "#") continue; const eq = line.indexOf("="); // 첫 '='에서만 나눈다 const key = line.slice(0, eq).replace(/[ \t]+$/, ""); let value = line.slice(eq + 1).replace(/^[ \t]+/, ""); let end = value.length; // 끝에서만, 네 글자 중 무엇이든 한꺼번에 뗀다 while (end > 0 && " \t,;".includes(value.charAt(end - 1))) end--; value = value.slice(0, end); if (value.length >= 2 && value[0] === value[value.length - 1] && (value[0] === '"' || value[0] === "'")) { value = value.slice(1, -1); // 같은 따옴표 한 쌍만 벗긴다 } out.push(`${key}=[${value}]`); count++;}out.push(String(count));console.log(out.join("\n"));
JavaMain.java · 42 줄
import java.io.BufferedReader;import java.io.InputStreamReader;public class Main { static boolean blank(char c) { return c == ' ' || c == '\t'; } // s[from, to)에서 양 끝의 빈칸과 탭을 뗀 조각 static String trim(String s, int from, int to) { while (from < to && blank(s.charAt(from))) from++; while (to > from && blank(s.charAt(to - 1))) to--; return s.substring(from, to); } public static void main(String[] args) throws Exception { BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); int n = Integer.parseInt(in.readLine().trim()); StringBuilder out = new StringBuilder(); int count = 0; for (int i = 0; i < n; i++) { String raw = in.readLine(); String line = trim(raw, 0, raw.length()); if (line.isEmpty() || line.charAt(0) == '#') continue; int eq = line.indexOf('='); // 첫 '='에서만 나눈다 String key = trim(line, 0, eq); String value = trim(line, eq + 1, line.length()); int end = value.length(); // 끝에서만, 네 글자 중 무엇이든 한꺼번에 뗀다 while (end > 0 && " \t,;".indexOf(value.charAt(end - 1)) >= 0) end--; value = value.substring(0, end); int len = value.length(); if (len >= 2 && value.charAt(0) == value.charAt(len - 1) && (value.charAt(0) == '"' || value.charAt(0) == '\'')) { value = value.substring(1, len - 1); // substring(시작, 끝): 같은 따옴표 한 쌍만 벗긴다 } out.append(key).append("=[").append(value).append("]\n"); count++; } out.append(count).append('\n'); System.out.print(out); }}
C#Main.cs · 29 줄
using System;using System.Text;class Program{ static void Main() { int n = int.Parse(Console.ReadLine()!.Trim()); var sb = new StringBuilder(); int count = 0; for (int i = 0; i < n; i++) { string line = Console.ReadLine()!.Trim(' ', '\t'); if (line.Length == 0 || line[0] == '#') continue; int eq = line.IndexOf('='); // 첫 '='에서만 나눈다 string key = line.Substring(0, eq).TrimEnd(' ', '\t'); string value = line.Substring(eq + 1).TrimStart(' ', '\t'); value = value.TrimEnd(' ', '\t', ',', ';'); // 끝에서만, 네 글자 중 무엇이든 한꺼번에 뗀다 if (value.Length >= 2 && value[0] == value[value.Length - 1] && (value[0] == '"' || value[0] == '\'')) { value = value.Substring(1, value.Length - 2); // Substring(시작, 길이): 같은 따옴표 한 쌍만 벗긴다 } sb.Append(key).Append("=[").Append(value).Append("]\n"); count++; } sb.Append(count).Append('\n'); Console.Write(sb); }}
C++main.cpp · 38 줄
#include <iostream>#include <string>// 양 끝에서 chars에 든 글자를 뗀다. 남는 글자가 없으면 npos가 나오므로 따로 처리한다static std::string trimLeft(const std::string& s, const char* chars) { std::size_t p = s.find_first_not_of(chars); return p == std::string::npos ? "" : s.substr(p);}static std::string trimRight(const std::string& s, const char* chars) { std::size_t p = s.find_last_not_of(chars); return p == std::string::npos ? "" : s.substr(0, p + 1);}int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::string line; std::getline(std::cin, line); int n = std::stoi(line); int count = 0; for (int i = 0; i < n; i++) { std::getline(std::cin, line); line = trimRight(trimLeft(line, " \t"), " \t"); if (line.empty() || line[0] == '#') continue; std::size_t eq = line.find('='); // 첫 '='에서만 나눈다 std::string key = trimRight(line.substr(0, eq), " \t"); std::string value = trimLeft(line.substr(eq + 1), " \t"); value = trimRight(value, " \t,;"); // 끝에서만, 네 글자 중 무엇이든 한꺼번에 뗀다 if (value.size() >= 2 && value.front() == value.back() && (value[0] == '"' || value[0] == '\'')) { value = value.substr(1, value.size() - 2); // substr(시작, 길이): 같은 따옴표 한 쌍만 벗긴다 } std::cout << key << "=[" << value << "]\n"; count++; } std::cout << count << "\n"; return 0;}
Cmain.c · 33 줄
#include <stdio.h>#include <string.h>static int blank(char c) { return c == ' ' || c == '\t'; }int main(void) { static char buf[256]; int n, count = 0; if (!fgets(buf, sizeof buf, stdin) || sscanf(buf, "%d", &n) != 1) return 0; for (int i = 0; i < n; i++) { if (!fgets(buf, sizeof buf, stdin)) break; char *a = buf; char *b = buf + strcspn(buf, "\r\n"); /* [a, b)가 줄의 내용 */ while (a < b && blank(*a)) a++; /* 줄의 양 끝을 다듬는다 */ while (b > a && blank(b[-1])) b--; if (a == b || *a == '#') continue; char *eq = memchr(a, '=', (size_t)(b - a)); /* 첫 '='에서만 나눈다 */ char *ke = eq; while (ke > a && blank(ke[-1])) ke--; /* 이름은 [a, ke) */ char *v = eq + 1; while (v < b && blank(*v)) v++; /* 끝에서만, 네 글자 중 무엇이든 한꺼번에 뗀다 */ while (b > v && (blank(b[-1]) || b[-1] == ',' || b[-1] == ';')) b--; if (b - v >= 2 && *v == b[-1] && (*v == '"' || *v == '\'')) { v++; /* 같은 따옴표 한 쌍만 벗긴다 */ b--; } printf("%.*s=[%.*s]\n", (int)(ke - a), a, (int)(b - v), v); count++; } printf("%d\n", count); return 0;}
Craykonmain.cray · 27 줄
n = 콘솔.줄읽기().수로변환();count = 0;반복 (i = 0; i < n; i++){ line = 콘솔.줄읽기().공백제거(); // 먼저 줄의 양 끝을 다듬는다 만약 (line == "" || line[0] == "#") { 건너뛰기; } eq = line.자리찾기("="); // 첫 '='에서만 나눈다 key = line.부분문자열(0, eq).공백제거(); value = line.부분문자열(eq + 1).공백제거(); end = value.길이; // 끝에서만, 네 글자 중 무엇이든 한꺼번에 뗀다 동안 (end > 0 && " \t,;".있나(value[end - 1])) { end -= 1; } value = value.부분문자열(0, end); len = value.길이; 만약 (len >= 2 && value[0] == value[len - 1] && (value[0] == "\"" || value[0] == "'")) { value = value.부분문자열(1, len - 2); // 부분문자열(시작, 길이): 같은 따옴표 한 쌍만 벗긴다 } 콘솔.쓰기(key + "=[" + value + "]"); count += 1;}콘솔.쓰기(count);