안녕하세요. 이번 시간에는 파이썬 입력 여러줄을 받는 방법을 알아봅시다. 보통, 최근에 나온 백준 문제들은 input의 갯수를 주거나, 끝을 알 수 있는 특정한 무언가가 있습니다. 그런데, 간혹 가다가, 파일의 끝을 판단해야 하는 경우가 있어요. 이럴 땐 어떻게 해야 할까요? 결론만 보시려면, 맨 밑에 단락만 보시면 되는데요. tracemalloc을 이용해서 trace 하는 것을 보시려면 글 전체를 읽어보시는 것도 좋습니다.

 

 파일의 line이 매우 많지만, 한 라인 당 글자수는 100자를 넘지 않는다고 가정해 봅시다.

 


 그 전에 tracemalloc은 메모리가 얼마나 할당되었는지 추적합니다. 문서를 보면, 자세히 알 수 있는데요. 이 글에서는 사용법을 상세히 알려드리진 않을 것입니다. 단지, 링크에 있는 코드를 참고하는 수준에서 진행해 보겠습니다.

 

 먼저 제너레이터는 아래와 같이 만들었습니다. 각 줄마다 [1, 10000] 구간에 속한 정수 2개를 공백으로 구분해서 출력했습니다. 각 인풋 파일의 줄 수는 100만, 200만, 400만 이런 식으로 조절하였습니다.

 

 제 디렉토리에 있는 1.in, 2.in, 3.in을 보면, 19M, 38M, 75M임을 알 수 있는데요. 1.in에 비해서 2.in과 3.in은 각각 2배, 4배 뻥튀기가 되었음을 알 수 있어요. 먼저 각각의 방법을 비교해 보겠습니다.

 

 7번째 줄을 보시면, sys.stdin.readlines()가 있는데요. 이것은 sys.stdin에 있는 것 전체를 읽어들입니다. 1.in, 2.in, 3.in이 있으니까, 리다이렉션을 시켜서 테스트 해 보겠습니다. python3 A.py < 1.in은, A.py가 입력을 1.in에서 받는다는 것을 의미합니다.

 

 128M, 257M, 514M가 보이네요. 뭔가 메모리를 파일의 크기에 비례해서 쓰는 것 같은 건 기분 탓이 아닐 겁니다. readlines의 결과를 저장해 놓는 변수 li를 trace 해 보면 알 수 있습니다.

 

 파일에 13 23, 15 339가 있었을 때입니다. li에 이러한 내용들이 몽땅 들어가 있습니다. 2개의 line을 더 추가해 봅시다. 100 200과 300 400을요. 그리고 나서, 다시 돌려보면 어떻게 될까요?

 

 li에는 13 23, 15 339, 100 200, 300 400이 저장되게 됩니다. 당연한 이야기겠지만, 파일의 크기가 매우 크다면, readlines로 파일 전체의 contents를 메모리에 올리는 것은 불가능에 가까울 겁니다. 디스크가 괜히 있는 것도 아니고요.

 


 어떻게 하면 될까요? 전체를 읽는 게 아니라, line 단위로 읽으면 됩니다. 한 라인 당 글자 수가 작다고 가정했기 때문입니다.

 

 아까와 다른 점은 sys.stdin.readlines() 대신에, sys.stdin을 썼다는 것입니다. 이래도 될까요? 

 

 

 실행해 보면 그래도 된다는 것을 알 수 있습니다. 심지어 메모리도 훨씬 적게 먹습니다. for x in sys.stdin이 하는 일은 여기 질답글에 언급이 되어 있긴 합니다. 여기서 짚어야 할 것은 핵심은 yield입니다. 루프를 돌면서, line을 하나씩 읽어옵니다. 생각해 보면 전체를 다 들고 올 필요가 없음을 알 수 있기도 하고요.

 

 물론 이렇게 짜셔도 문제는 없습니다. readline은 EOF를 만났을 때, 빈 문자열을 돌려주는데요. 빈 문자열인 경우에 if not s가 참이 됩니다. 메모리는 얼마나 먹을까요?

 

 전체를 읽어오지 않았으니, 메모리를 덜 먹을 겁니다.

 


 백준 10951번 문제를 풀어봅시다. 이 문제는 파일의 끝 말고는 입력의 끝을 판단할 방법이 없어요.

 

 방법은 간단합니다. 2번째 줄에서 sys.stdin을 돌리는데요. 이것도 파일 객체입니다. for loop를 돌릴 때 마다 line을 한 줄씩 읽어올 겁니다. 이제 한 줄씩 처리하면 됩니다.