
def show(title: str) -> None:
print("\n" + "=" * 10, title, "=" * 10)
def main() -> None:
show("type check")
a = 10
b = 3.14
c = "123"
d = True
print(a, type(a))
print(b, type(b))
print(c, type(c))
print(d, type(d))
show("type casting")
print(int("123"), type(int("123")))
print(float("3.14"), type(float("3.14")))
print(str(10), type(str(10)))
show("bool rules")
print(bool(""), bool("0"), bool("hello"))
print(bool(0), bool(1), bool(-1))
print(bool([]), bool([1]))
print(bool({}), bool({"a": 1}))
show("mission 1: ' 003 ' -> 3")
s = " 003 "
x = int(s.strip())
print(x, type(x))
show("mission 2: '3.0' -> 3")
y = int(float("3.0"))
print(y, type(y))
if __name__ == "__main__":
main()
def show(title: str) -> None:
print("\n" + "=" * 10, title, "=" * 10)
def main() -> None:
show("precedence basics")
print("10 + 2 * 3 =", 10 + 2 * 3)
print("(10 + 2) * 3 =", (10 + 2) * 3)
print("2 ** 3 ** 2 =", 2 ** 3 ** 2) # ์ค๋ฅธ์ชฝ ๊ฒฐํฉ
show("division")
print("7 / 2 =", 7 / 2)
print("7 // 2 =", 7 // 2)
print("7 % 2 =", 7 % 2)
show("comparisons + logic")
a, b = 12, 3
print("a > 10 and b < 5 =", a > 10 and b < 5)
print("not (a > 10 and b < 5) =", not (a > 10 and b < 5))
print("a == 12 or b == 5 =", a == 12 or b == 5)
show("mission: classify number")
n = 0
if n > 0:
print("positive")
elif n == 0:
print("zero")
else:
print("negative")
if __name__ == "__main__":
main()
def main() -> None:
name = "thsqh"
price = 12900
qty = 3
total = price * qty
print("\n1) f-string basics")
print(f"name={name}")
print(f"price={price}, qty={qty}, total={total}")
print("\n2) float formatting")
latency_ms = 123
print(f"latency={latency_ms}ms, latency_sec={latency_ms/1000:.3f}s")
print("\n3) alignment")
for x in [3, 30, 300]:
print(f"x right aligned: |{x:>6}|")
print(f"x left aligned: |{x:<6}|")
print("\n4) repr (!r) - ๊ณต๋ฐฑ/์ด์ค์ผ์ดํ ํ์ธ์ฉ")
s = " hello\tworld\n"
print(f"raw: {s!r}")
if __name__ == "__main__":
main()
def show(title: str) -> None:
print("\n" + "=" * 10, title, "=" * 10)
def extract_user_domain(email: str) -> tuple[str, str]:
email = email.strip()
at = email.find("@")
if at == -1:
raise ValueError(f"Invalid email: {email!r}")
user = email[:at]
domain = email[at + 1 :]
return user, domain
def clean_csv_like(s: str) -> list[str]:
# " a, b ,c " -> ["a", "b", "c"]
s = s.strip()
parts = [p.strip() for p in s.split(",")]
return parts
def parse_log(line: str) -> dict:
# ์: '2026-01-21 09:12:33 INFO user_id=42 status=200 path="/api/v1/auth"'
line = line.strip()
date, time, level, rest = line.split(" ", maxsplit=3)
out = {"date": date, "time": time, "level": level}
# path="...": ๋ฐ์ดํ ๊ฐ ๋จผ์ ์ถ์ถ
key = 'path="'
if key in rest:
start = rest.find(key) + len(key)
end = rest.find('"', start)
out["path"] = rest[start:end]
rest = (rest[: rest.find(key)] + rest[end + 1 :]).strip()
for kv in rest.split():
if "=" not in kv:
continue
k, v = kv.split("=", 1)
v = v.strip()
out[k] = int(v) if v.isdigit() else v
return out
def main() -> None:
show("indexing / slicing")
e = " admin@company.co.kr "
user, domain = extract_user_domain(e)
print(f"email={e!r} -> user={user!r}, domain={domain!r}")
print("domain first char:", domain[0])
print("domain last char:", domain[-1])
show("split / join / strip / replace")
s = " a, b ,c "
parts = clean_csv_like(s)
print("parts:", parts)
joined = "|".join(parts)
print("joined:", joined)
print("replaced:", joined.replace("b", "B"))
show("escape + formatting")
msg = "He said: \"hello\"\nNext\tTabbed"
print(msg)
print(f"repr: {msg!r}")
show("log parsing")
log = '2026-01-21 09:12:33 INFO user_id=42 action=login latency_ms=123 status=200 path="/api/v1/auth"'
print(parse_log(log))
if __name__ == "__main__":
main()
def parse_log(line: str) -> dict:
line = line.strip()
date, time, level, rest = line.split(" ", maxsplit=3)
out = {"date": date, "time": time, "level": level}
i = 0
n = len(rest)
while i < n:
# ๊ณต๋ฐฑ ์คํต
while i < n and rest[i] == " ":
i += 1
if i >= n:
break
# key ์ฝ๊ธฐ
eq = rest.find("=", i)
if eq == -1:
break
key = rest[i:eq]
i = eq + 1
# value ์ฝ๊ธฐ (๋ฐ์ดํ๋ฉด "..." ๋๊น์ง, ์๋๋ฉด ๊ณต๋ฐฑ ์ ๊น์ง)
if i < n and rest[i] == '"':
i += 1
endq = rest.find('"', i)
if endq == -1:
raise ValueError("Unclosed quote in log value")
val = rest[i:endq]
i = endq + 1
else:
# ๊ณต๋ฐฑ ์ ๊น์ง
j = i
while j < n and rest[j] != " ":
j += 1
val = rest[i:j]
i = j
out[key] = int(val) if val.isdigit() else val
return out
# src/day2_strings.py
import json
from pathlib import Path
from asyncio import log
def show(title: str) -> None:
print("\n" + "=" * 10, title, "=" * 10)
def extract_user_domain(email: str) -> tuple[str, str]:
email = email.strip()
at = email.find("@")
if at == -1:
raise ValueError(f"Invalid email: {email!r}")
user = email[:at]
domain = email[at + 1:]
return user, domain
def clean_csv_like(s: str) -> list[str]:
# " a, b ,c " -> ["a", "b", "c"]
s = s.strip()
parts = [p.strip() for p in s.split(",")]
return parts
def parse_log(line: str) -> dict:
line = line.strip()
date, time, level, rest = line.split(" ", maxsplit=3)
out = {"date": date, "time": time, "level": level}
i = 0
n = len(rest)
while i < n:
# ๊ณต๋ฐฑ ์คํต
while i < n and rest[i] == " ":
i += 1
if i >= n:
break
# key ์ฝ๊ธฐ
eq = rest.find("=", i)
if eq == -1:
break
key = rest[i:eq]
i = eq + 1
# value ์ฝ๊ธฐ (๋ฐ์ดํ๋ฉด "..." ๋๊น์ง, ์๋๋ฉด ๊ณต๋ฐฑ ์ ๊น์ง)
if i < n and rest[i] == '"':
i += 1
endq = rest.find('"', i)
if endq == -1:
raise ValueError("Unclosed quote in log value")
val = rest[i:endq]
i = endq + 1
else:
# ๊ณต๋ฐฑ ์ ๊น์ง
j = i
while j < n and rest[j] != " ":
j += 1
val = rest[i:j]
i = j
out[key] = int(val) if val.isdigit() else val
return out
def validate(d: dict) -> list[str]:
errors = []
status = d.get("status")
user_id = d.get("user_id")
path = d.get("path")
if not (isinstance(status, int) and 100 <= status <= 599):
errors.append(f"bad status={status}")
if not (isinstance(user_id, int) and user_id > 0):
errors.append(f"bad user_id={user_id}")
if not (isinstance(path, str) and path.startswith("/")):
errors.append(f"bad path={path!r}")
return errors
def main() -> None:
raw_path = Path("data/raw/sample.log")
ok_rows = []
bad_rows = []
for idx, line in enumerate(raw_path.read_text(encoding="utf-8").splitlines(), start=1):
if not line.strip():
continue
d = parse_log(line)
errs = validate(d)
if errs:
bad_rows.append({"line_no": idx, "errors": errs, "row": d})
else:
ok_rows.append(d)
Path("data/processed/parsed_logs.json").write_text(
json.dumps(ok_rows, ensure_ascii=False, indent=2),
encoding="utf-8"
)
Path("data/processed/parsed_logs_errors.json").write_text(
json.dumps(bad_rows, ensure_ascii=False, indent=2),
encoding="utf-8"
)
print(f"OK={len(ok_rows)}, BAD={len(bad_rows)}")
show("indexing / slicing")
e = " admin@company.co.kr "
user, domain = extract_user_domain(e)
print(f"email={e!r} -> user={user!r}, domain={domain!r}")
print("domain first char:", domain[0])
print("domain last char:", domain[-1])
show("split / join / strip / replace")
s = " a, b ,c "
parts = clean_csv_like(s)
print("parts:", parts)
joined = "|".join(parts)
print("joined:", joined)
print("replaced:", joined.replace("b", "B"))
show("escape + formatting")
msg = "He said: \"hello\"\nNext\tTabbed"
print(msg)
print(f"repr: {msg!r}")
show("log parsing")
log = '2026-01-21 09:12:33 INFO user_id=42 status=200 path="/api v1/auth" msg="login failed: bad password"'
print(parse_log(log))
d = parse_log(log)
assert 100 <= d.get("status", 0) < 599, f"bad status: {d.get('status')}"
assert d.get("user_id", 0) > 0, f"bad user_id: {d.get('user_id')}"
assert isinstance(d.get("path"), str) and d["path"].startswith(
"/"), f"bad path: {d.get('path')}"
print("quality checks: OK")
print(d)
out_path = Path("data/processed/parsed_log.json")
out_path.write_text(json.dumps(
d, ensure_ascii=False, indent=2), encoding="utf-8")
print(f"saved -> {out_path}")
if __name__ == "__main__":
main()