강의/computer science

[ComputerScience] c++의 file I/O Streams와 string

하기싫지만어떡해해야지 2024. 9. 23. 14:07

이 게시글은

서울대학교 데이터사이언스대학원

조요한 교수님의

데이터사이언스 응용을 위한 컴퓨팅 강의를

학습을 위해 재구성하였습니다.


저번시간은 c++의 입출력 시스템인

std::cin과 std::cout에 대해서 배웠었다

 

이번 시간에는

c++의 File I/O Streams에 대해 배워보자

 

c++의 File I/O Stream는

ifstream, ofstream을 사용한다

ifstream이 Input file system

ofstream이 output file system이다

 

헤더에는 #include <fstream>을 정의해줘야

사용할 수 있고

standard I/O Stream과 비슷한 방식으로

사용된다고한다

 

 

File Read

 

std::ifstream file(PATH)로 파일 변수를 선언해준다

이렇게 선언하면 읽어올 파일의 변수명은 file이 된다

 

파일이 제대로 열렸는지 확인하기위해

if(file.is_open())으로 확인해주고

제대로 열리지 않았을 때 cerr 메세지를 띄우게한다

 

std::getline 메소드를 통해서

파일의 각 라인을 읽어온다

위에 string 변수인 line을 정의해주고

while문 안에 getline(file, line)을 해주면

file에 있는 라인이 없을 때까지

while문을 돌면서 line이라는 string변수에 저장된다

 

마지막으로는 file.close()를 통해

파일을 닫아줘야한다

 

 

반드시 getline으로 읽어와야하는 것은 아니다

위 ppt의 예시를 통해

파일을 어떻게 받아오는지 이해해보자

 

앞 시간에 배웠듯이 input buffer로는

whitespace, tab이 있으면

그 앞까지만 읽어올 수 있다

 

그럼 예시에서 data.txt파일을

while(file >> number)와 같이

input buffer를 통해 가져오게 된다면 어떻게될까

 

우선 젤 처음 텍스트를 읽어올 것이기 때문에

while문을 1회 돌 때 number에는

1이 저장되고 출력된 뒤

whitespace 때문에 중단된다

 

그 다음 while문이 있기 때문에

다시 2번 돌게되고

number에는 1과 whitespace 다음 글자인 2가 

저장되고 출력된다

그 다음 다시 whitespace로 멈췄다가

다시 while문을 통해서 3번째 반복하고

마지막으로는 3을 출력하고 whlie문을 탈출하게된다

 

따라서 위 ppt처럼 output이 출력되는 것이다

 

std의 cin이나 cout처럼

본인 자신의 object를 계속해서 반환하면서

sequencial하게 이용이 가능하다

 

 

File Write

 

쓸 파일을

std::ofstream file(PATH)와 같이 정의해준다

그럼 쓸 파일의 변수명은 file이 된다

 

ifstream 때와 동일하게

file.open으로 에러여부 확인해준다음

 

<< 를 통해서 file에 쓰고싶은 내용을 넣어준다

쓸 내용을 다 넣었다면

file.close()로 닫아주면

지정해둔 경로에 file이 써진 것을 확인할 수 있을 것이다


string

c언어에서는 string이 없어서

char array로 사용해야하지만

c++은 string이라는 데이터타입이 존재한다

 

string을 구성하는 요소로는

data, size, capacity가 있다

 

data는 실제로 텍스트를 저장하고

있는 곳의 포인터 값으로

heap에 array로 저장되어있다

 

heap에 몇 개의 공간이 할당되었는지가

size 정보 (저장된 텍스트 데이터가 몇 칸인지)이고

최대 몇 개의 데이터를 담을 수 있는지를

나타내는 capacity가 있다

 

capacity는 어떻게 설정되냐는 질문이 있었는데

c++이 모든 컴퓨터나 환경에서 동일하지가 않다고 한다.

window에서 사용하는 c++이나

mac에서 사용하는 c++이나 모두 다르고

standard library도 기본적으로

지원하는 기능 정도만 정의해놓지

그 기능들이 어떻게 구현되는지는 각 환경이나 컴퓨터에따라

달라질 수 있다고한다

 

따라서 capacity같은 경우에도 c++ 라이브러리를

어디서 어떻게 구현하고 배포하느냐에따라

달라질 수 있기 때문에

정확하게 말할 수는 없다고한다

 

 

string을 초기화하는 방법에는

크게 세 가지가 있다

 

std::string str1;

이렇게 하면 빈 string이 선언되고

std::string str2 = "String2";

라고하면 String2라는 string이 선언된다

std::string str3("String3");

이렇게 선언해도 결과는 위와 마찬가지

 

 

 

string concatenation은 간단한데

+로 붙여주면 된다

+= 로도 붙여줄 수 있고

append 메소드를 이용할 수도 있다

 

 

 

string에서 비교하는 메소드이다

string에서의 대소관계는

알파벳, 가나다순이다

 

따라서 ==, <, >와 같이

크기를 비교하는 연산자들로

string끼리 비교할 수 있고

compare 메소드들로도 가능하다

 

 

string에서 substring을 찾는 법이다

find()를 이용하면 찾고자하는 substring이

전체 string에서 몇 번째 위치에 있는지 반환해준다

 

"Data Science"에서

"Science"는 5번째 인덱스부터

존재하기때문에

find를 하면 5가 반환된다

 

만약 substring이 존재하지 않는다면

string::npos 값을 반환하는데

string::npos값이란

size_t가 나타낼 수 있는 최대로 큰 값을 말한다

 

size_t는 unsigned integer 값으로

대충 string이 가질 수 있는 사이즈 정보를 담는

데이터 타입이라고 한다

 

아무튼

따라서 string에 substring이 존재하는지 아닌지는

if (pos != string::npos)와 같은 방식으로 확인한다

find한 값이 string::npos의 값과 같으면

substring이 없는거고

같지 않으면 substring이 특정 인덱스에

존재한다는 의미이다

 

 

substr(5, 3)은

5번째 index부터 3개를 가져오라는 뜻이다

따라서 "Data Science"에서

substr(5, 3)을 하면

"Sci"가 나오게 된다

 

 

replace(0, 4, "Bio")는

0번째 index부터 4개를 가져와서

"Bio"와 바꿔라

라는 의미이다

 

따라서 "Data Science"에서

replace(0, 4, "Bio")를 해주면

"Data" 대신에 "Bio"가 들어가게 된다

그래서 전체를 출력하면

"Bio Science"가 출력될 것이다

 

string에서 다른 데이터타입으로

변경하는 것도 가능하다

 

std::stoi를 이용하면

string에서 int로 변경하는 것이고

std::stod는

string에서 double로 변경하는 것이다

 

반대로 int나 double을 string으로 바꾸고싶다면

std::to_string 메소드를 이용할 수 있다

 

 

string에서의 메모리 구조이다

밑 코드를 잘 이해해보자

 

맨 처음 myString에는 "Hello World!"라는

string을 저장했다

그러고 myString의

size, capacity, 첫번째 글자의 주솟값을 출력해보니

13, 22, 그리고 어떤 주솟값이 나왔다

 

그 다음으로는 myString 뒤에 "!"를 추가했다

추가한 뒤 똑같이

size, capacity, 첫번째 주솟값을 출력하니

size는 "!"가 추가됐으므로 14로 늘어나고

capacity와 첫번째 주솟값은 그대로인 것을

확인할 수 있다

 

마지막으로는

"A large string"이라는 꽤 긴 문자를

myString에 추가했다

그 다음 size, capacity, 첫번째 주솟값을 출력하니

size는 긴 문자까지 추가된 29로 늘어났고

capacity가 기존에는 22였지만

myString의 size가 capacity를 넘겼으므로

capacity도 47로 늘어난 것을 확인할 수 있다

그리고 첫번째 문자의 주솟값이

기존의 myString과 달라진 것을 확인할 수 있다

 

이 메모리 주소가 왜 바뀌었냐하면

기존의 capacity로는

새로 추가된 myString을 담을 수 없으니

새로운 더 긴 array를 할당해서

capacity를 늘린 것이다

 

따라서 이를 통해

string을 다룰 때

기존 capacity보다

더 큰 사이즈를 저장하게되면

reallocation이 발생하게 된다는 것을

확인할 수 있다

 

 

stringstream은 string을 stream처럼

사용할 수 있게 해준다

위에 #include <sstream> 을 선언해서 사용 가능하다

위 ppt 예시처럼

std::stringstream parser("42,3.14,Hello World");로

stringstream을 선언하면

parser에는 뒤 문자가 stream으로 담긴다

 

그 다음 parser에 있는 stream을

각각

intValue

ignoreChar

doubleValue

ignoreChar

strValue

에 담으려고 한다

 

ignoreChar은 저장하려는 용도라기보단

,를 따로 빼려는 용도이다

 

parser >> intValue에서

intValue는 데이터타입이 int이기때문에

42만 저장이된다

이후 ignoreChar에는

바로 뒤 char 값인 콤마(,)가 저장이된다

 

이후 doubleValue에는 double값인

3.14까지 저장이 된다

그다음 ignoreChar에는 동일하게

콤마(,)가 저장이 된다

 

그리고 이후 strValue에 Hello World를

저장해주려하는데

Hello World안에 띄어쓰기가 있기때문에

단순한 input buffer를 이용해서 담아주면

Hello 까지만 저장이된다

 

그래서 getline을 통해서

strValue안에 Hello World를 가져와서 담아준다

 

 

반대로 stringstream ss를 정의한 후

100, 3.14, "Hello"를 넣어주면

ss에는 1003.14Hello가 담기게된다

 

하지만 이렇게 쓰는것이 권장되는 방법은 아니라고하는데

현재 ss를 read하는지 write하는지

이를 state를 가져와서 관리하는데

이게 계속해서 변경되면 작동이 복잡해지기때문이라고 한다

 

따라서 작동원리를 정확하게 알고있는게

아닌 이상 이런방식으로 자유롭게 쓰는 것은

권장되지 않는다고 한다