요약
이 수업은 텍스트와 파일을 다루는 강좌의 일곱째 단원을 연다. 단원이 무엇을 위한 것인지 설명한다. 텍스트를 실제 인코딩까지 포함한 데이터로 다루는 법과, 형식이 잘못된 입력 앞에서도 무너지지 않고 파일을 읽고 쓰는 프로그램을 만드는 법을 가르치는 것이다. 단원의 네 수업과 그것들을 잇는 실을 소개한다. 단원이 이미 안다고 전제하는 것, 특히 기계에게 문자는 수라는 사실, 그리고 5단원과 6단원의 벡터, 레코드, 함수를 되짚는다. 단원이 해결하는 문제를 제시한다. 올바르게 보이지만 아무 경고 없이 데이터를 잃고, 예상하지 못한 첫 줄에서 멈추고, ASCII 밖의 문자가 들어간 이름을 잘못 재는 프로그램의 문제이다. 한 글자가 늘 한 바이트를 차지하지는 않는다는 것을 미리 보여 준다. 그리고 단원이 8단원과 C++ 프로그래밍 II 강좌에서 어디로 이어지는지 보여 준다.
학습 목표
- 단원의 내용 순서를 설명하고, 각 수업이 뒤이은 수업에 무엇을 보태는지 설명한다.
- 문자를 수치 코드로 나타내는 방식(2.4강)을 떠올리고, 코드를 이진법과 십육진법으로 쓴다.
- 예를 통해 한 글자가 늘 한 바이트인 것은 아니며, 그래서 바이트를 세는 것이 글자를 세는 것이 아님을 미리 익힌다.
- 단원을 마쳤을 때 할 줄 알아야 하는 것이 무엇인지 알고, 그것을 뒤의 단원과 C++ 프로그래밍 II에서 배울 것과 구별한다.
입문 평가
단원을 시작하기 전에 출발점을 재어 두면 좋다. 이 평가는 자기 평가이며 점수를 매기지 않는다. 목적은 오직 학습의 방향을 잡는 데 있다. 처음 여섯 문항은 단원이 이미 안다고 전제하는 것을 확인한다. 문자의 코드, 수를 이진법과 십육진법으로 쓰는 법, 비트 마스크, 원소를 걸러 내는 벡터 순회, 읽기가 수 대신 글자를 받았을 때 일어나는 일, 그리고 레코드의 정의이다. 그 뒤의 네 문항은 수업 순서대로 단원의 핵심 개념을 다룬다. 아무것도 찾지 못한 검색이 돌려주는 것, ASCII 밖의 글자가 들어간 단어의 크기, 두 가지 읽기 방식을 섞을 때 나타나는 빈 줄, 그리고 열지 못한 파일이다. 이 네 문항을 아직 맞힐 의무는 누구에게도 없으며, 그중 몇 문항에서는 “아직 모르겠다”가 정직한 답이다. 맞든 틀리든 모든 답이 요점을 설명하고 그 요점을 다루는 수업을 알려 주기 때문이다. 같은 능력은 단원 끝의 종합 수업에서 다시 측정한다.
단원을 시작하기 전에 출발점을 확인해 보세요. 짧은 자기 평가입니다. 점수가 매겨지지 않으며 결과는 브라우저에만 저장됩니다.
문제 1이전 단원에서
std::println("{:d} {:d}", 'A', 'a');문장은 무엇을 출력하는가?소문자가 대문자 바로 뒤에 온다고 생각한다. 둘 사이에는 코드 32개가 있고, 그 사이에 기호 몇 개가 끼어 있다.{:d}지정이 코드를 요구한다는 것을 놓친다. 그 지정이 없으면std::println함수는 글자를 쓴다.순서를 뒤집는다. ASCII에서는 대문자의 코드가 소문자의 코드보다 작다.char타입은 수, 곧 문자의 코드를 저장하고,{:d}지정은 글자가 아니라 그 수를 쓰라고 요구한다. ASCII에서 대문자는 65부터, 소문자는 97부터 시작한다. 차이는 32, 곧 비트 하나이다. 텍스트는 ASCII가 아니다 수업은 여기서 출발하여 ASCII에 없는 글자에 어떤 일이 일어나는지 설명한다.문제 2이전 단원에서233을 이진법과 십육진법으로 어떻게 쓰는가?
아래쪽 네 비트를 잘못 묶는다.1001묶음은 1이 아니라 9이다.비트를 거꾸로, 가장 낮은 자리에서 가장 높은 자리로 읽는다.십육진 표기와 십진 표기를 혼동한다.. 오른쪽부터 네 개씩 묶으면1110묶음과1001묶음은 14와 9, 곧e문자와9문자이다. 이것은 “é”의 코드이며, 단원은 오늘날의 텍스트 파일에서 이 코드가 한 바이트e9값으로 저장되지 않고 두 바이트로 저장된다는 것을 보여 줄 것이다.복습: 정수와 그 표현
문제 3이전 단원에서
0xB1 & 0xC0식의 값은 얼마인가?&연산자를|연산자와 혼동한다. 비트 OR 연산은 상위 비트를 골라내는 대신 켠다.마스크가 아무것도 바꾸지 않는다고 생각한다. 마스크는 아래쪽 여섯 비트를 지운다.&연산자를 XOR 연산자인^연산자와 혼동한다.0xB1값은 이진수로10110001패턴,0xC0값은11000000패턴에 해당한다. 비트 AND 연산은0xB1값의 상위 두 비트만 남기며, 그 두 비트는10패턴이다. 이 마스크가 바로 텍스트는 ASCII가 아니다 수업이 한 글자를 이어 가는 바이트를 알아보는 데 쓰는 것이다.복습: 비트와 수학 라이브러리
문제 4이전 단원에서
std::vector<int> v{4, 7, 10, 3}; std::vector<int> w{}; for (const int x : v) { if (x % 2 == 0) { w.push_back(x); } } std::println("{}", w);코드는 무엇을 출력하는가?조건을 뒤집는다.x % 2 == 0조건은 짝수일 때 참이다.push_back함수가 벡터 전체를 복사한다고 생각하거나, 거르기가v벡터에 적용되고v벡터가 출력된다고 생각한다.push_back함수가 맨 앞에 추가한다고 생각한다. 추가되는 곳은 맨 끝이다.반복문은v벡터를 순회하면서 짝수만 나타난 순서대로w벡터에 복사한다.v벡터는 바뀌지 않는다. 단원은 텍스트에도 같은 틀을 쓴다. 문자열의 바이트를 순회하며 그중 일부를 남기거나, 파일의 줄을 순회하며 올바른 줄을 남기는 식이다.복습: 벡터
문제 5이전 단원에서
int n{-1}; std::cin >> n;코드 뒤에 사용자가abc세 글자를 입력한다.n변수의 값은 얼마이며, 그 뒤의 읽기는 어떻게 되는가?실패한 읽기는 흔적을 남기지 않는다고 생각한다. 흔적은 변수와 스트림의 상태에 남는다.C++ 언어가 잘못된 입력을 알려 준다고 가정한다. 프로그램은 읽기가 성공한 것처럼 계속된다.정수 읽기와 문자 읽기를 혼동한다.읽기는 공백을 건너뛰고, 수를 시작할 수 없는 글자를 만나 실패한다. 표준은 변수가 0을 받고, 그 글자가 스트림에 남고, 스트림이 오류 상태가 된다고 정하며, 아무 메시지도 없다. 입력을 제대로 읽기 수업은 이 실패를 데이터의 끝과 구별하고 거기서 복구하는 법을 가르친다.복습: 입력, 출력, 오류
문제 6이전 단원에서아래의 정의 가운데 제목과 연도를 가진 레코드를 올바르게 선언하는 것은 어느 것인가?
닫는 중괄호 뒤의 세미콜론을 잊는다. 레코드를 쓰는 사람이 가장 먼저 저지르는 오류이며, 컴파일러는 이를 정확히 짚어 준다.목록의 원소처럼 필드를 쉼표로 구분한다. 각 필드는 하나의 선언이며 세미콜론으로 끝난다.자료형이 서로 다르고 이름이 붙은 필드를 가진 레코드를, 원소가 모두 같은 자료형인 벡터와 혼동한다.문제 7
s변수가std::string s{"casa azul"};선언으로 만들어졌다면,s.find("x")호출은 무엇을 돌려주는가?다른 언어들의 관례이다.std::size_t타입은 부호가 없고, 이 타입으로 변환된-1값은 바로npos값이지만, 결과를-1값과 비교하는 것은 이 변환 덕분에만 통하며 컴파일러 경고를 일으킨다.“찾지 못함”을 위치 0과 혼동한다. 위치 0은 문자열 맨 앞에서 나타났을 때의 위치이다.정직한 답이다. 7.1강에서find함수와npos값을 소개한다.문제 8이 강좌의 프로그램처럼 UTF-8로 저장된 프로그램에서
std::println("{}", std::string{"año"}.size());문장은 무엇을 출력하는가?글자를 센다.size함수는 그 글자들을 쓰는 데 쓰인 바이트를 센다.모든 글자가 두 바이트를 차지한다고 생각한다. ASCII 글자는 한 바이트를 차지한다.정직한 답이다. 7.2강에서 바이트 단위로 설명한다.size함수는 글자가 아니라 바이트를 센다. 스페인어 단어 “año”(해)에서 “a”와 “o”는 각각 한 바이트를 차지하고, ASCII에 없는 “ñ”는 UTF-8에서 두 바이트를 차지한다. 텍스트는 ASCII가 아니다 수업이 그 이유를 설명하고 글자를 세는 법을 가르친다.복습: 텍스트는 ASCII가 아니다
문제 9한 프로그램이
int age{}; std::string name{}; std::cin >> age; std::getline(std::cin, name);코드를 실행하고, 사용자가36값을 입력하고 Enter 키를 누른 뒤지은이름을 입력하고 Enter 키를 누른다.name변수에는 무엇이 남는가?getline함수가 새 줄에서 시작한다고 가정한다. 시작하는 곳은>>연산자가 멈춘 곳이다.getline함수가>>연산자가 이미 소비한 것을 다시 읽는다고 생각한다.정직한 답이다. 7.3강은 이 결함에서 시작한다.>>연산자는 36을 읽고 그 뒤에 오던 줄바꿈 문자를 스트림에 남긴다.std::getline함수는 첫 줄바꿈 문자, 곧 그 문자까지 읽고 그 앞에 있던 것을 돌려준다. 아무것도 없다. 입력을 제대로 읽기 수업이 이를 설명하고std::ws조작자로 피한다.복습: 입력을 제대로 읽기
문제 10한 프로그램이
std::ifstream in{"data.txt"}; int x{-1}; in >> x;코드를 실행하지만, 프로그램을 실행하는 폴더에data.txt파일이 없다. 무슨 일이 일어나는가?없는 파일을 여는 것이 C++ 언어가 보고하는 오류라고 가정한다. 프로그램은 아무 경고 없이 계속된다.읽기 위한 열기와 쓰기 위한 열기를 혼동한다. 쓰기 위한 열기는 실제로 파일을 만든다.정직한 답이다. 7.4강은 이 확인에서 시작한다.
콘텐츠를 불러오는 중…