• [C++] 하나의 파일을 이용하여 다른 한 개의 파일에 추가적인 입력과 수정하기

    2023. 9. 11.

    by. KAEY


    ┃요구 사항

    member.txt 파일에 저장되어 있는 사용자의 이름과 비밀번호를 기반으로

          사용자에게 아이디와 비밀번호를 입력받아 로그인 성공 여부를 결정한다.

    로그인 성공 되었다면, 해당 사용자에게 전화번호를 입력받아 사용자의 이름과

          전화번호를 member_tel.txt 파일에 새로 저장한다.

    (1) member_tel.txt 을 검사하여 사용자가 기존에 없었다면 새롭게 추가한다.

    (1-1) 단 수정할 때, 새롭게 추가된 사람은 가장 위로 저장되어야 한다. 

     (2) member_tel.txt 에 이미 사용자가 있다면, 입력받은 전화번호로 수정한다.

     

     

     


    ✨ INTRO 

    여러 개념을 함께 사용해서 하면 충분히 구현이 될 것 같았다.

    구현할 때, 기존의 실습과는 다르게 여러 번 파일스트림을 써서 읽어오고 저장하고 닫아야하는 과정과

    이를 변수들에 담고 원하는 형태로 가공해서 사용하는 과정에서 제대로 값이 담기는지 확인하면서 하느라

    좀 애를 먹었다. 

     

    또한 테스트를 하는 과정에서 txt 파일에 직접적으로 접근해서 수정하고 이를 비쥬얼 스튜디오에서 접근할 때

    값이 제대로 인식되지 않았던 경우가 있었는데, 이 부분 때문에 굉장히 많은 시간이 걸렸다.

    (예를 들어 값이 있음에도 수정되지 않거나 했음. cout을 통해 직접적으로 값을 확인해도 문제가 없었는데,

    뭔가 이 간격 사이에 문제가 있었던 것 같았다. 정상적으로 스튜디오 내에서 값을 저장했을 땐 문제가 발생하지 않았음.)

     

    분명 짜놓은 논리 상으로는 문제가 없는 것 같았는데, 문제가 생기니 내가 설계한 게 잘못된건가 계속 다시 보게 되고.

    어디가 잘못된건지 하나 하나 확인해봐도 문제가 없었는데 실행하니 원하는 형태로 되지 않아서 정말 애먹었다.

    (A=3 이고 B=5 라고 선언했는데, A+B = 8로 나오지 않았던 상황과 유사함.)

     

    과정에서는 화가 났는데, 막상 풀고나니까... 기분 좋아서 뭔가 내 자신이 밉다. 😂

    요구 사항이 많아서 코드 자체가 길어지니 생각보다 변수 이름 선언할 때 직관적인 이름을 붙여야겠단 생각도 들었다.

     

     

     


    ✨구현

    ✅ member.txt 파일에 저장되어 있는 사용자의 이름과 비밀번호를 기반으로

    	string line;
    	while (getline(file_read, line)) { // get_line() file의 내용을 한 줄씩 읽어옴
    		stringstream ss(line); 
            //공백을 기준으로 저장된 파일이므로 stringtream을 사용
    		string token1, token2;
            //각각 토큰1은 id값을, 토큰2는 비밀번호를 담음 (한줄씩 읽을 때)
    		if (ss >> token1 >> token2) {
    			info_id.push_back(token1);
    			info_pw.push_back(token2);
                //이를 각각 백터로 선언한 info_ 값에 넣음
    		}
    	}

    member 파일의 값은 공백을 기준으로 아이디와 비밀번호를 저장하는 규칙을 가지고 있다.

    그러므로 stringstream 을 통해서 하나씩 값을 추출하고 이를 사전에 선언해놓은 info_XX 의 벡터에

    하나 씩 저장한다.

    따라서 info_id[0] 과 info_pw[0] 의 값은 한 사람의 아이디와 비밀번호를 가지게 된다.

    이를 통해 info_XX[ i ] 로 반복문을 실행하게 되면, 한 사람의 아이디와 비밀번호를 탐색할 수 있게 된다.

    자료 구조에서 key 와 value 의 구조를 이용한다면 편할 것이다.

    하지만 지금 구현할 땐 강의 진도에서 key 와 value를 다루지 않았으므로 이와 비슷한 개념으로 설계했다.

     

     

     

    cout << "아이디 [1]" << info_id[1];

    이 때, 값이 예상한 대로 저장되어 있는지 확인하기 위해 사용했던 cout 문.

     

     

     

     사용자에게 아이디와 비밀번호를 입력받아 로그인 성공 여부를 결정한다.

    	bool exit_flag = false;
    
    	while (1) {
    		cout << "아이디를 입력해주세요. ";
    		cin >> input_id;
            
           		for( .... ){
            		......        
                    }
            
    		if (exit_flag)
    		{
    			break;
    		}
    	}

    사용자에게 입력 받은 아이디와 비밀번호를 기반으로 이것이 일치하는지 검증하는 반복문.

    반복문이 두 개 사용될 것이기에 처음으로 감싸는 부분에 아이디를 입력하는 과정을 사용함.

    또한 이중 반복문이고 로그인이 성공되었을 때, 해당 반복문이 끝나도 되므로.

    (실패한다면, 아이디를 입력하는 과정으로 돌아감.)

    로그인 성공에 대한 깃발을 세우고 이를 기준으로 로그인 성공에 대한 종료를 선언하였음.

     

     

     

    for (int i = 0; i < info_id.size(); i++) {
    	if (input_id == info_id[i]) { 
        //맴버의 아이디값과 입력값이 같음.
        //input_id는 사용자가 입력한 아이디를 저장한 변수
    		cout << "비밀번호를 입력하세요 : ";
    		cin >> input_pw;
    
    		if (input_pw == info_pw[i]) {
    			cout << "\n ***로그인 성공. " << endl;
    			exit_flag = true; 
                //로그인 맨 처음 부분을 종료하기 위한 지점 설정
    			break;
                //해당 아이디 비밀번호 일치 과정을 종료 후 다시 아이디 입력으로
    		}
    		else {
    			cout << "\n ***비밀번호가 다릅니다. 로그인 실패. \n" << endl;
    			break;
                //해당 아이디 비밀번호 일치 과정을 종료 후 다시 아이디 입력으로
    		}
    	}
    	else {
    		cout << "\n ***예상치못한 오류 발생 code 44~66. " << endl;
            //예상치 못한 오류가 발생했을 때, 해당 코드가 어디인지 표시
    	}

    로그인할 때 아이디와 비밀번호가 member_id.txt 에 저장된 값과 일치하는지 검증하는 과정.

     

     


    ✅ 로그인 성공 되었다면, 해당 사용자에게 전화번호를 입력받아 사용자의 이름과

          전화번호를 member_tel.txt 파일에 새로 저장한다. (사용자가 기존에 없었다면 새롭게 추가한다.)

    	while (getline(read_file, line2)) { // get_line() file의 내용을 한 줄씩 읽어옴
    		stringstream ss(line2);
    		string token1, token2;
    		if (ss >> token1 >> token2) {
    			tel_id.push_back(token1);
    			tel_tel.push_back(token2);
    		}
    	}

    기존에 사용자가 있었는지 먼저 확인해야 하기 때문에 위에서 했던 것과 같이,

    member_tel.txt 파일의 값을 tel_id, tel_tel 로 선언된 벡터에 저장하여 검증할 것임.

    따라서, tel_id 와 input_id (처음에 로그인 당시 사용자가 입력한 아이디를 담은 변수)가 일치하면,

    member_tel.txt 에 사용자가 이미 있었다는 의미이고, 이 경우는 추가가 아닌 수정(업데이트) 해야함.

     

     

     

    ✨ 놓칠 수 있는 예외 사항 : member_tel.txt 파일이 아예 비어있는 경우

    	if (tel_id.size() == 0) {
    		file << input_id << " " << user_tel << "\n";
    		cout << "처음으로 입력되었습니다." << endl;
    	}

    tel.id 에 값이 저장되어야 하는데, 저장된 벡터의 size() 가 0 이라면 member_tel.txt 은

    아무런 값이 입력되지 않은 상태라는 것이다. 따라서 이 경우 예외 처리가 필요하다.

    처음으로 입력된 상황 처리를 하지 않을 경우, 검증하는 과정에서 오류가 발생할 수 있다.

     

     

     

    ✅ 새롭게 추가된 사람은 가장 위로 저장되어야 한다. 

     

    가장 생소했던 개념... 이미 저장된 파일에서 가장 위로 올리는 걸 어떻게 구현해야 하나

    고민하는 시간이 제일 길었다. 내가 생각해서 구현해놓은 논리는 

     

    (1) 기존의 파일에 입력된 값들을 싹 다 배열에 저장한다.

    (2) 새로운 배열 파일에 새로 입력된 값을 추가한다. (새로 입력된 값 하나만 추가됨.)

    (3) 1에서 저장한 모든 배열을 순서대로 붙여서 새로 입력된 값 뒤로 추가한다.

     

    이며, 위와 같이 설계 하였다.

    1의 경우 기존에 stringstream 을 통해서 이미 추출한 배열이라, 또 사용하기만 하면 되는거라

    이 경우가 가장 간단할 것이라 생각했다.

     

    	for (int i = 0; i < tel_id.size(); i++) {
    		if (check == 0) {
    			....
    		}
    		file << tel_id[i] << " " << tel_tel[i] << "\n";
            	//기존의 벡터를 새로운 내용 뒤에 붙임
    	}

    따라서 위와 같이 if 문을 통해서 새로 추가하거나 수정하는 경우가 끝났을 때, 

    반복문을 통해서 추가된 내용 뒤로 기존의 member_tel 의 값을 모두 붙여넣을 수 있도록 하였다.

     

     

     

     (2) member_tel.txt 에 이미 사용자가 있다면, 입력받은 전화번호로 수정한다.

    	for (int i = 0; i < tel_id.size(); i++) {		
    		if (input_id == tel_id[i]) { //기존에 있던 사용자란 의미
    			tel_tel[i] = "";
    			tel_tel[i] = user_tel;
    			cout << "수정 되었씁니다." << endl;
    			check = 1;
    		}
    	}

    사용자가 이미 있는지를 반복문을 통해 확인하고, 있다면 해당 사용자의 비밀번호가 저장되어 있는

    곳의 벡터 값을 공백으로 수정 후, 입력된 전화번호를 넣는 형식으로 하였다.

    또한 이 과정에서 수정했기에, 입력된 값으로 추가적인 처리를 하지 않도록 check 값을 부여함.

     

     

     

     (1) member_tel.txt 을 검사하여 사용자가 기존에 없었다면 새롭게 추가한다.

    	for (int i = 0; i < tel_id.size(); i++) {
    		if (check == 0) {
           	 	//check == 0은 기존에 없었던 사용자란 의미.
    			file << input_id << " " << user_tel << "\n";
    			cout << "새로 추가되었습니다." << endl;
    			check = 1;
    		}
    		file << tel_id[i] << " " << tel_tel[i] << "\n";
    	}

    위에서 사용자가 이미 있는지 확인하는 과정을 거쳐도 사용자가 없다는 의미인 check = 0 일 땐

    새롭게 가장 위쪽에 요구 내용 (사용자 명 + 전화번호 ) 을 추가한다.

     

    그 후 모든 과정 (새로운 입력 or 업데이트)이 끝나면, 기존의 member_tel.txt 값들을 새로 추가된

    내용의 뒤로 붙여서 요구 사항 1-1 과 같이 가장 위로 저장된 형태를 갖게 된다.

     

     

     

    출력화면 : 

     

     

     

    ✨코드 전문 (깃 링크)

         🐱‍👤 해당 문구를 클릭시 이동합니다. (새창으로 열림)

     

     

     


    🎉해당 글의 내용은 "포스코 x 코딩온 스마트 팩토리 과정 수업" 에서의 수업 자료 및 실습 과제 등에서 일부 발췌되어 작성 되었습니다.


    댓글 (비로그인 댓글 허용하지 않습니다.)