Skip to Content
💻 코리아IT아카데미 신촌 - 프로그래밍 학습 자료
C++ 프로그래밍Unit 14: 파일 입출력과 스트림Topic 4: 문자열 스트림과 변환

Topic 4: 문자열 스트림과 변환 - 문자열 마법사 되기 🔄

🎯 학습 목표

  • stringstream의 개념과 동작 원리를 이해할 수 있다
  • 문자열과 숫자 간의 자유로운 변환을 할 수 있다
  • 복잡한 문자열을 파싱하고 데이터를 추출할 수 있다
  • istringstream과 ostringstream의 차이점을 구분할 수 있다
  • 실제 게임 명령어 시스템을 구현할 수 있다

📝 메모장과 stringstream

stringstream = 메모리 속 가상 메모장 📱💭

일반적인 파일 입출력:

// 실제 파일에 쓰기 ofstream file("data.txt"); file << "Hello World";

stringstream 사용:

// 메모리 속 가상 파일에 쓰기 stringstream ss; ss << "Hello World"; string result = ss.str(); // "Hello World"

메모장 비유:

  • 파일 스트림 = 실제 종이 노트에 쓰기 📔
  • stringstream = 휴대폰 메모장에 쓰기 📱
  • 더 빠르고, 임시 작업에 완벽! ⚡

🎪 stringstream의 3형제

1. stringstream - 읽기/쓰기 겸용 🔄

stringstream ss; ss << "data"; // 쓰기 string result; ss >> result; // 읽기

2. ostringstream - 쓰기 전용 ✍️

ostringstream oss; oss << "output data"; string result = oss.str();

3. istringstream - 읽기 전용 📖

string data = "input data"; istringstream iss(data); string word; iss >> word;

🔢 문자열 ↔ 숫자 변환의 마법

숫자를 문자열로 변환

#include <iostream> #include <sstream> #include <string> using namespace std; int main() { // 다양한 타입을 문자열로 변환 int playerLevel = 25; double playerHealth = 87.5; bool isAlive = true; stringstream ss; // 숫자들을 문자열로 변환 ss << "플레이어 레벨: " << playerLevel << " | "; ss << "체력: " << playerHealth << "% | "; ss << "상태: " << (isAlive ? "생존" : "사망"); string gameStatus = ss.str(); cout << "🎮 " << gameStatus << endl; // 또 다른 방법: 개별 변환 ostringstream scoreStream; scoreStream << "점수: " << 98500 << "점"; string scoreText = scoreStream.str(); cout << "🏆 " << scoreText << endl; return 0; }

실행 결과:

🎮 플레이어 레벨: 25 | 체력: 87.5% | 상태: 생존 🏆 점수: 98500점

문자열을 숫자로 변환

#include <iostream> #include <sstream> #include <string> using namespace std; int main() { // 사용자 입력 시뮬레이션 string userInput = "25 87.5 true"; istringstream iss(userInput); int level; double health; string aliveStr; // 문자열에서 데이터 추출 iss >> level >> health >> aliveStr; bool isAlive = (aliveStr == "true"); cout << "📊 파싱 결과:" << endl; cout << "레벨: " << level << endl; cout << "체력: " << health << "%" << endl; cout << "생존 여부: " << (isAlive ? "생존 😊" : "사망 💀") << endl; return 0; }

🎮 실전 예제: 게임 세이브 데이터 파서

복잡한 세이브 데이터 파싱하기

#include <iostream> #include <sstream> #include <string> #include <vector> using namespace std; struct PlayerData { string name; int level; double health; int gold; vector<string> inventory; }; class SaveDataParser { public: // 플레이어 데이터를 문자열로 직렬화 static string serialize(const PlayerData& player) { ostringstream oss; // 기본 정보 oss << player.name << "|" << player.level << "|" << player.health << "|" << player.gold << "|"; // 인벤토리 아이템들 (콤마로 구분) for (size_t i = 0; i < player.inventory.size(); i++) { oss << player.inventory[i]; if (i < player.inventory.size() - 1) { oss << ","; } } return oss.str(); } // 문자열을 플레이어 데이터로 파싱 static PlayerData deserialize(const string& data) { PlayerData player; istringstream iss(data); string token; // | 구분자로 기본 정보 파싱 getline(iss, player.name, '|'); getline(iss, token, '|'); player.level = stoi(token); // string to int getline(iss, token, '|'); player.health = stod(token); // string to double getline(iss, token, '|'); player.gold = stoi(token); // 인벤토리 파싱 string inventoryData; getline(iss, inventoryData); if (!inventoryData.empty()) { istringstream inventoryStream(inventoryData); string item; while (getline(inventoryStream, item, ',')) { player.inventory.push_back(item); } } return player; } // 플레이어 정보 출력 static void printPlayer(const PlayerData& player) { cout << "⚔️ =========================" << endl; cout << " 플레이어: " << player.name << endl; cout << " 레벨: " << player.level << " 📊" << endl; cout << " 체력: " << player.health << "% ❤️" << endl; cout << " 골드: " << player.gold << " 💰" << endl; cout << " 인벤토리: "; if (player.inventory.empty()) { cout << "(비어있음) 📦"; } else { for (size_t i = 0; i < player.inventory.size(); i++) { cout << player.inventory[i]; if (i < player.inventory.size() - 1) { cout << ", "; } } cout << " 🎒"; } cout << endl; cout << "⚔️ =========================" << endl; } }; int main() { // 원본 플레이어 데이터 PlayerData hero; hero.name = "DragonSlayer"; hero.level = 42; hero.health = 87.5; hero.gold = 1250; hero.inventory = {"강철검", "체력포션", "마법반지", "용의비늘"}; cout << "🎮 원본 플레이어 데이터:" << endl; SaveDataParser::printPlayer(hero); // 직렬화 (객체 → 문자열) string saveData = SaveDataParser::serialize(hero); cout << "\n💾 직렬화된 데이터:" << endl; cout << saveData << endl; // 파일에 저장 시뮬레이션 cout << "\n📁 파일에 저장 중..." << endl; cout << "✅ 저장 완료!" << endl; // 역직렬화 (문자열 → 객체) cout << "\n📂 데이터 로딩 중..." << endl; PlayerData loadedPlayer = SaveDataParser::deserialize(saveData); cout << "\n🔄 로드된 플레이어 데이터:" << endl; SaveDataParser::printPlayer(loadedPlayer); return 0; }

실행 결과:

🎮 원본 플레이어 데이터: ⚔️ ========================= 플레이어: DragonSlayer 레벨: 42 📊 체력: 87.5% ❤️ 골드: 1250 💰 인벤토리: 강철검, 체력포션, 마법반지, 용의비늘 🎒 ⚔️ ========================= 💾 직렬화된 데이터: DragonSlayer|42|87.5|1250|강철검,체력포션,마법반지,용의비늘 📁 파일에 저장 중... ✅ 저장 완료! 📂 데이터 로딩 중... 🔄 로드된 플레이어 데이터: ⚔️ ========================= 플레이어: DragonSlayer 레벨: 42 📊 체력: 87.5% ❤️ 골드: 1250 💰 인벤토리: 강철검, 체력포션, 마법반지, 용의비늘 🎒 ⚔️ =========================

🎯 고급 예제: 게임 명령어 시스템

실시간 게임 명령어 처리기

#include <iostream> #include <sstream> #include <string> #include <vector> #include <map> using namespace std; class GameCommandSystem { private: map<string, int> playerStats; vector<string> inventory; public: GameCommandSystem() { // 기본 스탯 초기화 playerStats["level"] = 1; playerStats["hp"] = 100; playerStats["mp"] = 50; playerStats["gold"] = 100; } // 명령어 파싱 및 실행 void processCommand(const string& command) { istringstream iss(command); string action; iss >> action; // 첫 번째 단어가 액션 cout << "🎮 명령어: " << command << endl; if (action == "stat") { showStats(); } else if (action == "add") { string type; int amount; iss >> type >> amount; addStat(type, amount); } else if (action == "buy") { string item; int price; iss >> item >> price; buyItem(item, price); } else if (action == "inventory" || action == "inv") { showInventory(); } else if (action == "save") { saveGame(); } else if (action == "load") { string saveData; // 실제로는 파일에서 읽어야 하지만, 여기서는 직접 입력 getline(iss, saveData); loadGame(saveData); } else if (action == "help") { showHelp(); } else { cout << "❌ 알 수 없는 명령어입니다. 'help'를 입력해보세요." << endl; } cout << endl; } private: void showStats() { cout << "📊 === 캐릭터 스탯 ===" << endl; cout << "레벨: " << playerStats["level"] << " ⭐" << endl; cout << "HP: " << playerStats["hp"] << " ❤️" << endl; cout << "MP: " << playerStats["mp"] << " 💙" << endl; cout << "골드: " << playerStats["gold"] << " 💰" << endl; } void addStat(const string& type, int amount) { if (playerStats.find(type) != playerStats.end()) { playerStats[type] += amount; cout << "✅ " << type << "이(가) " << amount << " 증가했습니다!" << endl; cout << "현재 " << type << ": " << playerStats[type] << endl; } else { cout << "❌ 알 수 없는 스탯입니다: " << type << endl; } } void buyItem(const string& item, int price) { if (playerStats["gold"] >= price) { playerStats["gold"] -= price; inventory.push_back(item); cout << "✅ " << item << "을(를) " << price << "골드에 구매했습니다!" << endl; cout << "남은 골드: " << playerStats["gold"] << " 💰" << endl; } else { cout << "❌ 골드가 부족합니다! (필요: " << price << ", 보유: " << playerStats["gold"] << ")" << endl; } } void showInventory() { cout << "🎒 === 인벤토리 ===" << endl; if (inventory.empty()) { cout << "(비어있음)" << endl; } else { for (size_t i = 0; i < inventory.size(); i++) { cout << (i + 1) << ". " << inventory[i] << endl; } } } void saveGame() { ostringstream saveData; // 스탯 저장 saveData << playerStats["level"] << "," << playerStats["hp"] << "," << playerStats["mp"] << "," << playerStats["gold"] << "|"; // 인벤토리 저장 for (size_t i = 0; i < inventory.size(); i++) { saveData << inventory[i]; if (i < inventory.size() - 1) { saveData << ";"; } } string saveString = saveData.str(); cout << "💾 게임 저장 완료!" << endl; cout << "세이브 데이터: " << saveString << endl; } void loadGame(const string& saveData) { if (saveData.empty()) { cout << "❌ 세이브 데이터가 비어있습니다!" << endl; return; } istringstream iss(saveData); string statsData, inventoryData; // | 구분자로 스탯과 인벤토리 분리 getline(iss, statsData, '|'); getline(iss, inventoryData); // 스탯 로드 istringstream statsStream(statsData); string token; if (getline(statsStream, token, ',')) { playerStats["level"] = stoi(token); } if (getline(statsStream, token, ',')) { playerStats["hp"] = stoi(token); } if (getline(statsStream, token, ',')) { playerStats["mp"] = stoi(token); } if (getline(statsStream, token, ',')) { playerStats["gold"] = stoi(token); } // 인벤토리 로드 inventory.clear(); if (!inventoryData.empty()) { istringstream invStream(inventoryData); string item; while (getline(invStream, item, ';')) { inventory.push_back(item); } } cout << "📂 게임 로드 완료!" << endl; } void showHelp() { cout << "🆘 === 도움말 ===" << endl; cout << "stat - 캐릭터 스탯 보기" << endl; cout << "add [스탯] [수치] - 스탯 증가 (예: add hp 20)" << endl; cout << "buy [아이템] [가격] - 아이템 구매 (예: buy 검 100)" << endl; cout << "inv 또는 inventory - 인벤토리 보기" << endl; cout << "save - 게임 저장" << endl; cout << "load [데이터] - 게임 로드" << endl; cout << "help - 도움말" << endl; } }; int main() { GameCommandSystem game; string command; cout << "🎮 === 게임 명령어 시스템 시작 === 🎮" << endl; cout << "'help'를 입력하면 사용 가능한 명령어를 볼 수 있습니다." << endl; cout << "'quit'을 입력하면 종료됩니다." << endl << endl; while (true) { cout << "명령어 입력 > "; getline(cin, command); if (command == "quit" || command == "exit") { cout << "👋 게임을 종료합니다!" << endl; break; } if (!command.empty()) { game.processCommand(command); } } return 0; }

사용 예시:

명령어 입력 > stat 📊 === 캐릭터 스탯 === 레벨: 1 ⭐ HP: 100 ❤️ MP: 50 💙 골드: 100 💰 명령어 입력 > add hp 50 ✅ hp이(가) 50 증가했습니다! 현재 hp: 150 명령어 입력 > buy 강철검 80 ✅ 강철검을(를) 80골드에 구매했습니다! 남은 골드: 20 💰 명령어 입력 > inv 🎒 === 인벤토리 === 1. 강철검 명령어 입력 > save 💾 게임 저장 완료! 세이브 데이터: 1,150,50,20|강철검

🔄 문자열 조작 고급 테크닉

CSV 데이터 파싱하기

#include <iostream> #include <sstream> #include <vector> #include <string> using namespace std; struct GameRecord { string playerName; int score; string date; double playTime; }; vector<GameRecord> parseCSV(const string& csvData) { vector<GameRecord> records; istringstream dataStream(csvData); string line; // 헤더 라인 건너뛰기 getline(dataStream, line); while (getline(dataStream, line)) { istringstream lineStream(line); string token; GameRecord record; // 플레이어명 getline(lineStream, record.playerName, ','); // 점수 getline(lineStream, token, ','); record.score = stoi(token); // 날짜 getline(lineStream, record.date, ','); // 플레이 시간 getline(lineStream, token, ','); record.playTime = stod(token); records.push_back(record); } return records; } int main() { string csvData = R"(PlayerName,Score,Date,PlayTime DragonSlayer,95000,2024-03-15,2.5 MagicMaster,87500,2024-03-14,1.8 SwordHero,76200,2024-03-13,3.2 ArrowAce,68900,2024-03-12,2.1)"; cout << "📊 CSV 데이터 파싱 결과:" << endl; cout << "========================" << endl; vector<GameRecord> records = parseCSV(csvData); for (const auto& record : records) { cout << "플레이어: " << record.playerName << endl; cout << "점수: " << record.score << "점 🏆" << endl; cout << "날짜: " << record.date << " 📅" << endl; cout << "플레이 시간: " << record.playTime << "시간 ⏰" << endl; cout << "------------------------" << endl; } return 0; }

💡 핵심 정리

  • stringstream: 문자열을 스트림처럼 다루는 클래스
  • ostringstream: 출력 전용, 문자열 생성에 특화
  • istringstream: 입력 전용, 문자열 파싱에 특화
  • str(): stringstream의 내용을 string으로 반환
  • getline(stream, str, delimiter): 구분자 단위로 문자열 분리
  • stoi(), stod(): 문자열을 숫자로 변환하는 함수
  • 직렬화: 객체를 문자열로, 역직렬화: 문자열을 객체로

✅ 실습 체크리스트

🚀 다음 시간 예고

Unit-14 파일 입출력과 스트림을 모두 마쳤습니다! 🎉 다음 단원에서는 새로운 주제로 여러분과 만날 예정이에요.

지금까지 배운 내용들:

  • 📝 파일 스트림 기초: 데이터를 영구 저장하는 방법
  • 💾 이진 파일과 직렬화: 효율적인 데이터 저장 기법
  • 🎨 스트림 조작자와 포매팅: 아름다운 출력 만들기
  • 🔄 문자열 스트림과 변환: 문자열 처리의 고수 되기

“문자열 스트림으로 데이터 처리의 달인이 되었습니다! 🧙‍♂️✨”

Last updated on