논리가 맞았는데, 어디에서 틀린건지 모르겠어서이다.
여기서 내가 틀렸음을 쉽게 인정하고 물러나면, 나는 코테에서도 비슷하게 행동할지도 모른다.
한 발자국만 더 나아가면, 정답으로 바로 갈 수 있는데도 말이다.포기하지 말자. 원래 미래는 보이지 않고 불안하다.
1. HTML 파싱
- 난이도
- 골드 3
- 출처
A. 📜 문제
위 백준 사이트에 접속하여 문제를 확인해주세요.
B. 💡 내 답안
a. 😅 1차 시도 (실패)
def div(html: str) -> None:
"""
div 태그가 닫는 태그가 아니라면 title을 출력.
"""
if "/div" not in html.replace(' ', ''):
# title의 형태가 title="A"라서 왼쪽에서 count하는 index와 오른쪽에서 count하는 index를 사용하여 슬라이싱함.
text = "title : " + html[html.index('"')+1:html.rindex('"')]
print(text)
def p(html: str) -> None:
"""
p 태그가 존재하는 문장에 여는 괄호와 닫는 괄호를 구함.
[i번째 닫는 괄호: i+1번째 여는 괄호]로 슬라이싱하면, 태그 사이에 text를 추출할 수 있음
text가 공백으로만 이루어진 경우를 제외하고 texts 리스트에 추가.
texts 리스트는 공백이 2번이상인 경우 1개로 변환.
"""
starts, ends = find_start_end(html)
texts = []
for i, value in enumerate(starts[1:]):
start = value
end = ends[i]
# if html[end+1:start].strip():
texts.append(html[end+1:start])
text = ''.join(texts).strip()
while " " in text:
text = text.replace(" ", ' ')
print(text)
def open(html: str) -> None:
"""
html에서 문단을 나누는 div와 문장을 의미하는 p 태그를 파싱함.
즉, 현재 태그를 파악하고 원하는 태그만 선별.
"""
starts, ends = find_start_end(html)
for i, value in enumerate(starts):
start = value
end = ends[i]
# print(html[start:end+1])
# '/ div'같은 태그를 제외하기 위해 replace함
if "div" in html[start:end+1].replace(' ', ''):
div(html[start:end+1])
# 'p' 태그 사이에 다양한 태그가 올 수 있어서, 'p'태그의 여는 태그와 닫는 태그는 p_end, p_start에 따로 저장
if "/p" in html[start:end+1].replace(' ', ''):
p_end = end
p(html[p_start:p_end+1])
elif "p" in html[start:end+1].replace(' ', ''):
p_start = start
def find_start_end(html: str) -> list:
"""
문서에 존재하는 모든 여는 괄호(<)와 닫는 괄호(>)의 index를 구함.
여는 괄호가 존재하면 반드시 닫는 괄호가 존재하기 때문에 starts와 ends의 길이는 동일함.
"""
starts = []
ends = []
for i, value in enumerate(html):
if value == "<":
starts.append(i)
elif value == ">":
ends.append(i)
return starts, ends
if __name__ == "__main__":
html = input()
# print(html)
open(html)
b. 😊 2차 시도 (성공 - 문자열 - 구현)
def div(html: str) -> None:
"""
div 태그가 닫는 태그가 아니라면 title을 출력.
"""
if "</div>" not in html:
# title의 형태가 title="A"라서 왼쪽에서 count하는 index와 오른쪽에서 count하는 index를 사용하여 슬라이싱함.
text = "title : " + html[html.index('"')+1:html.rindex('"')]
print(text)
def p(html: str) -> None:
"""
p 태그가 존재하는 문장에 여는 괄호와 닫는 괄호를 구함.
[i번째 닫는 괄호: i+1번째 여는 괄호]로 슬라이싱하면, 태그 사이에 text를 추출할 수 있음
text가 공백으로만 이루어진 경우를 제외하고 texts 리스트에 추가.
texts 리스트는 공백이 2번이상인 경우 1개로 변환.
"""
starts, ends = find_start_end(html)
texts = ''
for i, value in enumerate(starts[1:]):
start = value
end = ends[i]
# if html[end+1:start].strip():
texts += html[end+1:start]
text = ' '.join(texts.split())
print(text)
def open(html: str) -> None:
"""
html에서 문단을 나누는 div와 문장을 의미하는 p 태그를 파싱함.
즉, 현재 태그를 파악하고 원하는 태그만 선별.
"""
starts, ends = find_start_end(html)
p_start = 0
p_end = 0
for i, value in enumerate(starts):
start = value
end = ends[i]
# print(html[start:end+1])
# '/ div'같은 태그를 제외하기 위해 replace함
if "<div title=" in html[start:end+1]:
div(html[start:end+1])
# 'p' 태그 사이에 다양한 태그가 올 수 있어서, 'p'태그의 여는 태그와 닫는 태그는 p_end, p_start에 따로 저장
if "</p>" in html[start:end+1]:
p_end = end
p(html[p_start:p_end+1])
elif "<p>" in html[start:end+1]:
p_start = start
def find_start_end(html: str) -> list:
"""
문서에 존재하는 모든 여는 괄호(<)와 닫는 괄호(>)의 index를 구함.
여는 괄호가 존재하면 반드시 닫는 괄호가 존재하기 때문에 starts와 ends의 길이는 동일함.
"""
starts = []
ends = []
for i, value in enumerate(html):
if value == "<":
starts.append(i)
elif value == ">":
ends.append(i)
return [starts, ends]
if __name__ == "__main__":
html = input()
open(html)
c. 😊 3차 시도 (성공 - 정규식)
import re
if __name__ == '__main__':
html_doc = input()
html_doc = html_doc[len('<main>'): -len('</main>')]
html_doc = re.sub(r'<div +title="([\w ]*)">', r'title : \1\n', html_doc)
html_doc = re.sub(r'</div>', '', html_doc)
html_doc = re.sub(r'<p>', '', html_doc)
html_doc = re.sub(r'</p>', '\n', html_doc)
html_doc = re.sub(r'</?[\w ]*>', '', html_doc)
html_doc = re.sub(r' ?\n ?', '\n', html_doc)
html_doc = re.sub(r' {2,}', ' ', html_doc)
print(html_doc)
d. 🙄 회고
내 풀이
- 맞았는데... 왜 틀렸지?
반성
- 논리는 맞았다.
- 다만, 작은 구멍을 놓쳤다.
결론
- 작은 구멍도 다 매꿀정도로 코드를 잘 짜야겠다.
나는 스터디원의 도움으로 정규식으로 이 문제를 해결했다. 그런데 왜 맨 처음 틀린 코드로 돌아가서 5%에 발생하던 value error
을 해결하려고 했을까?
논리가 맞았는데, 어디에서 틀린건지 모르겠어서이다.
여기서 내가 틀렸음을 쉽게 인정하고 물러나면, 나는 코테에서도 비슷하게 행동할지도 모른다.
한 발자국만 더 나아가면, 정답으로 바로 갈 수 있는데도 말이다.
포기하지 말자. 원래 미래는 보이지 않고 불안하다.
C. 🧐 문제 해설
이해한 내용을 바탕으로 작성했습니다.
a. 공통 해설
<div>
,</div>
로 문단을 나눈다.<div>
의 속성으로는title
이 존재한다.<div title="(A)">
형태이며,title : (A)
형식으로 출력해야 한다.(A)
는 항상 알파벳(a-z, A-Z), 언더바(_), 공백( )으로 구성되어 있다.
<p>
,</p>
로 문장을 나눈다.<p>
태그 안에는 다양한 태그들이 존재할 수 있다.- 해당 태그들은 모두 지운다.
- 태그들은 아직 닫히지 않은 태그가 있을 때, 다른 닫는 태그가 올 수 없다.
- ex)
<b><i></b></i>
(x),<b><i></i></b>
- ex)
<p>
태그 안에 존재하는 문장의 시작과 끝에 존재하는 공백은 지운다.- 문장은 알파벳(a-z, A-Z), 공백( )으로 주어진다.
- 공백이 2개 이상 연속된다면 하나의 공백으로 바꾼다.
<p>
,</p>
태그도 지운다.
- 태그를 표시하는
<
와>
사이에는 소문자 알파벳(a-z), 공백( ), 슬래시(/)로 이루어져 있다.
문제 자체는 그렇게 어렵지 않다.
다만, 우리의 상상력이 문제를 어렵게 만든다.
나는 알파벳(a-z, A-Z), 언더바(_), 공백( )으로 구성을 잘못 이해하여 이런 상상을 했다.
<div>
가 아닌 <d i v>
, </ div>
가 테스트 케이스로 입력되는건가?
그러나, 수십번 시도해보고 내린 결론은 그렇지 않다였다.
문제에는 대장 태그 2개(<div>
, <p>
)가 나온다.
이번 문제에서는 이 두 대장 태그에는 장난을 치지 않았다.
그냥 <div>
, <p>
문자 그대로이다.
만약, 5%에서 알 수 없는 이유로 자꾸 틀린다면, 아래 테스트 케이스를 실행해보라.
<main><div title="title_name_1"><p>paragraph 1</p><p>paragraph 2 <diving>Italic Tag</diving> <br > </p><p>paragraph 3 <b>Bold Tag</b> end.</p></div><div title="title_name_2"><p>paragraph 4</p><p>paragraph 5 <i>Italic Tag 2</i> <br > end.</p></div></main>
b. 문자열 - 구현
문제를 간단히 생각하고 풀어보자.
나는 <
와 >
를 모두 구해놓고 풀었다.
태그를 판단할 때는 <
a>
이런 구성으로 판단하였고, <p>
태그에서 Text를 추출할때는 >
text<
이런 방법으로 추출했다.
c. 정규식
정규식은 스터디원이 아이디어를 제공해줘서 풀어봤다.
코드가 상당히 간결한게, 정규식을 잘 이해한다면 대단한 무기가 될 수 있겠다는 생각도 들었다.
나는 아래 3개의 사이트를 참고해서 정규식을 적용했다.
re — 정규식 연산 — Python 3.10.2 문서
07-3 강력한 정규 표현식의 세계로 - 점프 투 파이썬 (wikidocs.net)
여담으로, 정규식을 사용하지 않고도 정규식만큼 간결하게 코드를 작성할 수 있다. 해당 코드는 문제를 해결해보고 백준 사이트를 잘 참고해보면 될 듯 하다.
참고문헌
baekjoon. 22859번: HTML 파싱 (acmicpc.net). Baekjoon. (accessed Feb 12, 2022)
Ghost