수색…


소개

C ++ 파일 입출력은 스트림을 통해 수행됩니다. 핵심 추상화는 다음과 같습니다.

텍스트를 읽기위한 std::istream .

텍스트 작성을위한 std::ostream .

std::streambuf 문자 읽기 또는 쓰기.

형식화 된 입력operator>> 사용합니다.

형식화 된 출력operator<< 사용합니다.

스트림은 std::locale . 예를 들어, 외부 인코딩과 내부 인코딩 간의 형식화 및 변환에 대한 자세한 내용을 참조하십시오.

스트림에 대한 추가 정보 : <iostream> 라이브러리

파일 열기

파일 열기는 모든 3 개의 파일 스트림 ( ifstream , ofstreamfstream )에 대해 동일한 방식으로 수행됩니다.

생성자에서 직접 파일을 열 수 있습니다.

std::ifstream ifs("foo.txt");  // ifstream: Opens file "foo.txt" for reading only.

std::ofstream ofs("foo.txt");  // ofstream: Opens file "foo.txt" for writing only.

std::fstream iofs("foo.txt");  // fstream:  Opens file "foo.txt" for reading and writing.

또는 파일 스트림의 멤버 함수 open() 사용할 수 있습니다.

std::ifstream ifs;
ifs.open("bar.txt");           // ifstream: Opens file "bar.txt" for reading only.

std::ofstream ofs;
ofs.open("bar.txt");           // ofstream: Opens file "bar.txt" for writing only.

std::fstream iofs;
iofs.open("bar.txt");          // fstream:  Opens file "bar.txt" for reading and writing.

파일이 성공적으로 열렸는지 항상 확인해야합니다 (작성하는 경우에도). 실패는 다음을 포함 할 수 있습니다 : 파일이 존재하지 않고, 파일이 올바른 액세스 권한을 가지고 있지 않고, 파일이 이미 사용 중이며, 디스크 오류가 발생하고, 드라이브 연결이 끊어져 있습니다. 검사는 다음과 같이 수행 할 수 있습니다.

// Try to read the file 'foo.txt'.
std::ifstream ifs("fooo.txt");  // Note the typo; the file can't be opened.

// Check if the file has been opened successfully.
if (!ifs.is_open()) {
    // The file hasn't been opened; take appropriate actions here.
    throw CustomException(ifs, "File could not be opened");
}

파일 경로에 백 슬래시 (예 : Windows 시스템)가 있으면 올바르게 이스케이프 처리해야합니다.

// Open the file 'c:\folder\foo.txt' on Windows.
std::ifstream ifs("c:\\folder\\foo.txt"); // using escaped backslashes
C ++ 11

원시 리터럴을 사용하십시오.

// Open the file 'c:\folder\foo.txt' on Windows.
std::ifstream ifs(R"(c:\folder\foo.txt)"); // using raw literal

대신 슬래시를 사용하십시오.

// Open the file 'c:\folder\foo.txt' on Windows.
std::ifstream ifs("c:/folder/foo.txt");
C ++ 11

현재 Windows에서 경로에 비 ASCII 문자가있는 파일을 열려면 비표준 와이드 문자 경로 인수를 사용할 수 있습니다.

// Open the file 'пример\foo.txt' on Windows.
std::ifstream ifs(LR"(пример\foo.txt)"); // using wide characters with raw literal

파일에서 읽기

파일에서 데이터를 읽는 방법에는 여러 가지가 있습니다.

데이터의 형식을 알면 스트림 추출 연산자 ( >> )를 사용할 수 있습니다. foo.txt 파일에 다음 데이터가 들어 있다고 가정 해 보겠습니다.

John Doe 25 4 6 1987
Jane Doe 15 5 24 1976

그런 다음 다음 코드를 사용하여 파일에서 해당 데이터를 읽을 수 있습니다.

// Define variables.
std::ifstream is("foo.txt");
std::string firstname, lastname;
int age, bmonth, bday, byear;

// Extract firstname, lastname, age, bday month, bday day, and bday year in that order.
// Note: '>>' returns false if it reached EOF (end of file) or if the input data doesn't
// correspond to the type of the input variable (for example, the string "foo" can't be
// extracted into an 'int' variable).
while (is >> firstname >> lastname >> age >> bmonth >> bday >> byear)
    // Process the data that has been read.

스트림 추출 연산자 >> 는 모든 문자를 추출하고 저장할 수없는 문자 또는 특수 문자 인 경우 중지합니다.

  • 문자열 유형의 경우 연산자는 공백 ( ) 또는 줄 바꿈 ( \n ).
  • 숫자의 경우, 연산자는 숫자가 아닌 문자에서 중지합니다.

즉, foo.txt 파일의 다음 버전도 이전 코드에서 성공적으로 읽을 수 있습니다.

John 
Doe 25
4 6 1987


Jane
Doe 
15 5
24
1976

스트림 추출 연산자 >> 항상 주어진 스트림을 반환합니다. 따라서 데이터를 연속적으로 읽으려면 여러 운영자를 함께 연결할 수 있습니다. 그러나 스트림을 부울 표현식으로 사용할 수도 있습니다 (이전 코드에서 while 루프에 표시된 것처럼). 스트림 클래스에는 bool 유형의 변환 연산자가 있기 때문입니다. 이 bool() 연산자는 스트림에 오류가없는 한 true 를 반환 true . 스트림이 오류 상태가되면 (예를 들어 더 이상 데이터를 추출 할 수 없기 때문에) bool() 연산자는 false 를 반환합니다. 따라서 이전 코드의 while 루프는 입력 파일을 끝까지 읽은 후에 종료됩니다.

전체 파일을 문자열로 읽으려면 다음 코드를 사용할 수 있습니다.

// Opens 'foo.txt'.
std::ifstream is("foo.txt");
std::string whole_file;

// Sets position to the end of the file.
is.seekg(0, std::ios::end);

// Reserves memory for the file.
whole_file.reserve(is.tellg());

// Sets position to the start of the file.
is.seekg(0, std::ios::beg);

// Sets contents of 'whole_file' to all characters in the file.
whole_file.assign(std::istreambuf_iterator<char>(is),
  std::istreambuf_iterator<char>());

이 코드는 불필요한 메모리 할당을 줄이기 위해 string 공간을 예약합니다.

파일을 한 줄씩 읽으려는 경우 getline() 함수를 사용할 수 있습니다.

std::ifstream is("foo.txt");   

// The function getline returns false if there are no more lines.
for (std::string str; std::getline(is, str);) {
    // Process the line that has been read.
}

고정 된 수의 문자를 읽으려면 스트림의 멤버 함수 인 read() 사용할 수 있습니다.

std::ifstream is("foo.txt");
char str[4];

// Read 4 characters from the file.
is.read(str, 4);

읽기 명령을 실행 한 후에는 오류 상태 플래그 failbit 이 설정되었는지 여부를 항상 확인해야합니다. 오류가 발생했는지 여부를 나타냅니다. 이것은 파일 스트림의 멤버 함수 fail() 을 호출하여 수행 할 수 있습니다.

is.read(str, 4); // This operation might fail for any reason.

if (is.fail())
    // Failed to read!

파일에 쓰기

파일에 쓰는 방법에는 여러 가지가 있습니다. 가장 쉬운 방법은 출력 파일 스트림 ( ofstream )을 스트림 삽입 연산자 ( << )와 함께 사용하는 것입니다.

std::ofstream os("foo.txt");
if(os.is_open()){
    os << "Hello World!";
}

<< 대신 출력 파일 스트림의 멤버 함수 write() 를 사용할 수도 있습니다.

std::ofstream os("foo.txt");
if(os.is_open()){
    char data[] = "Foo";

    // Writes 3 characters from data -> "Foo".
    os.write(data, 3);
}

스트림에 badbit 후에 오류 상태 플래그 badbit 가 설정되었는지 여부는 작업이 실패했는지 여부를 나타 내기 때문에 항상 확인해야합니다. 이것은 출력 파일 스트림의 멤버 함수 bad() 를 호출하여 수행 할 수 있습니다.

os << "Hello Badbit!"; // This operation might fail for any reason.
if (os.bad())
    // Failed to write!

모드 열기

파일 스트림을 만들 때 열기 모드를 지정할 수 있습니다. 시작 모드는 기본적으로 스트림이 파일을 여는 방법을 제어하는 ​​설정입니다.

(모든 모드는 std::ios 네임 스페이스에서 찾을 수 있습니다.)

열기 모드는 파일 스트림의 생성자 또는 open() 멤버 함수의 두 번째 매개 변수로 제공 될 수 있습니다.

std::ofstream os("foo.txt", std::ios::out | std::ios::trunc);

std::ifstream is;
is.open("foo.txt", std::ios::in | std::ios::binary);

올바른 기본값을 가지고 있더라도 iostream 멤버가 암시 적으로 설정하지 않으므로 다른 플래그를 설정하려면 ios::in 또는 ios::out 을 설정해야합니다.

시작 모드를 지정하지 않으면 다음 기본 모드가 사용됩니다.

  • ifstream - in
  • ofstream out
  • fstream - inout

디자인에 의해 지정할 수있는 파일 열기 모드는 다음과 같습니다.

방법 의미 에 대한 기술
app 추가하다 산출 파일 끝에 데이터를 추가합니다.
binary 이진 입출력 입력 및 출력은 2 진으로 수행됩니다.
in 입력 입력 읽을 파일을 엽니 다.
out 산출 산출 쓰기 위해 파일을 엽니 다.
trunc 잘라 내지하다 입출력 열 때 파일 내용을 제거합니다.
ate 끝에 입력 열 때 파일의 끝으로 이동하십시오.

참고 : binary 모드를 설정하면 데이터를 그대로 읽고 쓸 수 있습니다. 이를 설정하지 않으면 개행 문자 '\n' 을 플랫폼 특정 행 끝 시퀀스로 /에서 변환 할 수 있습니다.

예를 들어 Windows에서 행의 끝 시퀀스는 CRLF ( "\r\n" )입니다.
쓰기 : "\n" => "\r\n"
읽기 : "\r\n" => "\n"

파일 닫기

C ++에서는 명시 적으로 파일을 닫는 것이 거의 필요하지 않습니다. 파일 스트림은 소멸자의 관련 파일을 자동으로 닫을 것이기 때문입니다. 그러나 파일 스트림 개체의 수명을 제한하여 파일 핸들을 필요 이상으로 열어 두지 않도록해야합니다. 예를 들어, 모든 파일 작업을 자체 범위 ( {} )에 넣으면 다음과 같이 수행 할 수 있습니다.

std::string const prepared_data = prepare_data();
{
    // Open a file for writing.
    std::ofstream output("foo.txt");

    // Write data.
    output << prepared_data;
}  // The ofstream will go out of scope here.
   // Its destructor will take care of closing the file properly.

close() 명시 적으로 호출하는 것은 나중에 동일한 fstream 객체를 재사용하려고하지만 파일을 중간에 유지하지 않으려는 경우에만 필요합니다.

// Open the file "foo.txt" for the first time.
std::ofstream output("foo.txt");

// Get some data to write from somewhere.
std::string const prepared_data = prepare_data();

// Write data to the file "foo.txt".
output << prepared_data;

// Close the file "foo.txt".
output.close();

// Preparing data might take a long time. Therefore, we don't open the output file stream
// before we actually can write some data to it.
std::string const more_prepared_data = prepare_complex_data();

// Open the file "foo.txt" for the second time once we are ready for writing.
output.open("foo.txt");

// Write the data to the file "foo.txt".
output << more_prepared_data;

// Close the file "foo.txt" once again.
output.close();

스트림 플러시

파일 스트림은 다른 많은 유형의 스트림과 마찬가지로 기본적으로 버퍼링됩니다. 즉, 스트림에 대한 쓰기로 인해 기본 파일이 즉시 변경되지 않을 수도 있습니다. 버퍼링 된 모든 쓰기가 즉시 수행되도록하려면 스트림을 플러시 할 수 있습니다. flush() 메서드를 호출하거나 std::flush stream manipulator를 통해 직접이 작업을 수행 할 수 있습니다.

std::ofstream os("foo.txt");
os << "Hello World!" << std::flush;

char data[3] = "Foo";
os.write(data, 3);
os.flush();

스트림을 플러시하는 개행 쓰기를 결합하는 스트림 조작자 std::endl 이 있습니다.

// Both following lines do the same thing
os << "Hello World!\n" << std::flush;
os << "Hello world!" << std::endl;

버퍼링은 스트림에 쓰는 성능을 향상시킬 수 있습니다. 따라서 쓰기 작업이 많은 응용 프로그램에서는 불필요하게 플러시가 발생하지 않아야합니다. 반대로 I / O가 자주 수행되지 않으면 응용 프로그램은 스트림 객체에 데이터가 고착되지 않도록 자주 플러시를 고려해야합니다.

ASCII 파일을 std :: string으로 읽음

std::ifstream f("file.txt");

if (f)
{
  std::stringstream buffer;
  buffer << f.rdbuf();
  f.close();

  // The content of "file.txt" is available in the string `buffer.str()`
}

rdbuf() 메서드는 stringstream::operator<< 멤버 함수를 통해 buffer 푸시 될 수있는 streambuf 대한 포인터를 반환합니다.


Scott Meyers의 Effective STL 에서 대중화 된 또 다른 가능성은 다음과 같습니다.

std::ifstream f("file.txt");

if (f)
{
  std::string str((std::istreambuf_iterator<char>(f)),
                  std::istreambuf_iterator<char>());

  // Operations on `str`...
}

작은 코드가 필요하기 때문에 (그리고 문자열을 비롯한 모든 STL 컨테이너로 직접 파일을 읽을 수 있지만 큰 파일에는 속도가 느릴 수 있기 때문에)

참고 : 문자열 생성자에 대한 첫 번째 인수 주변의 여분의 괄호는 가장 까다로운 구문 분석 문제를 방지하는 데 필수적입니다.


마지막으로 중요한 것은 :

std::ifstream f("file.txt");

if (f)
{
  f.seekg(0, std::ios::end);
  const auto size = f.tellg();

  std::string str(size, ' ');
  f.seekg(0);
  f.read(&str[0], size); 
  f.close();

  // Operations on `str`...
}

아마도 가장 빠른 옵션 일 것입니다.

컨테이너에 파일 읽기

아래 예제에서는 std::stringoperator>> 를 사용하여 파일에서 항목을 읽습니다.

    std::ifstream file("file3.txt");

    std::vector<std::string>  v;

    std::string s;
    while(file >> s) // keep reading until we run out
    {
        v.push_back(s);
    }

위의 예제에서 operator>> 사용하여 한 번에 하나의 "항목"을 읽는 파일을 반복합니다. 동일한 효과는 스트림에서 한 번에 하나의 "항목"을 읽는 입력 반복자 인 std::istream_iterator 를 사용하여 수행 할 수 있습니다. 또한 대부분의 컨테이너는 두 개의 반복자를 사용하여 만들 수 있으므로 위의 코드를 다음과 같이 단순화 할 수 있습니다.

    std::ifstream file("file3.txt");

    std::vector<std::string>  v(std::istream_iterator<std::string>{file},
                                std::istream_iterator<std::string>{});

std::istream_iterator 대한 템플릿 매개 변수로 읽으려는 객체를 지정하기 만하면 원하는 모든 객체 유형을 읽을 수 있습니다. 따라서 우리는 단순히 위의 내용을 다음과 같이 (단어가 아닌) 읽기 라인으로 확장 할 수 있습니다 :

// Unfortunately there is  no built in type that reads line using >>
// So here we build a simple helper class to do it. That will convert
// back to a string when used in string context.
struct Line
{
    // Store data here
    std::string data;
    // Convert object to string
    operator std::string const&() const {return data;}
    // Read a line from a stream.
    friend std::istream& operator>>(std::istream& stream, Line& line)
    {
        return std::getline(stream, line.data);
    }
};


    std::ifstream file("file3.txt");

    // Read the lines of a file into a container.
    std::vector<std::string>  v(std::istream_iterator<Line>{file},
                                std::istream_iterator<Line>{});

포맷 된 텍스트 파일에서`struct '를 읽습니다.

C ++ 11
struct info_type
{
    std::string name;
    int age;
    float height;
    
    // we define an overload of operator>> as a friend function which
    // gives in privileged access to private data members 
    friend std::istream& operator>>(std::istream& is, info_type& info)
    {
        // skip whitespace
        is >> std::ws;
        std::getline(is, info.name);
        is >> info.age;
        is >> info.height;
        return is;
    }
};

void func4()
{
    auto file = std::ifstream("file4.txt");

    std::vector<info_type> v;

    for(info_type info; file >> info;) // keep reading until we run out
    {
        // we only get here if the read succeeded
        v.push_back(info);
    }

    for(auto const& info: v)
    {
        std::cout << "  name: " << info.name << '\n';
        std::cout << "   age: " << info.age << " years" << '\n';
        std::cout << "height: " << info.height << "lbs" << '\n';
        std::cout << '\n';
    }
}

file4.txt

Wogger Wabbit
2
6.2
Bilbo Baggins
111
81.3
Mary Poppins
29
154.8

산출:

name: Wogger Wabbit
 age: 2 years
height: 6.2lbs

name: Bilbo Baggins
 age: 111 years
height: 81.3lbs

name: Mary Poppins
 age: 29 years
height: 154.8lbs

파일 복사 중

std::ifstream  src("source_filename", std::ios::binary);
std::ofstream  dst("dest_filename",   std::ios::binary);
dst << src.rdbuf();
C ++ 17

C ++ 17에서 파일을 복사하는 표준 방법은 <filesystem> 헤더를 포함하고 copy_file 사용하는 것입니다.

std::fileystem::copy_file("source_filename", "dest_filename");

파일 시스템 라이브러리는 원래 boost.filesystem 으로 개발되었고 마지막으로 C ++ 17에서 ISO C ++로 병합되었습니다.

루프 상태, 나쁜 연습 안에 파일의 끝을 확인?

eof 는 파일의 끝을 읽은 후에true 반환합니다. 다음 읽기가 스트림의 끝임을 나타내지는 않습니다.

while (!f.eof())
{
  // Everything is OK

  f >> buffer;

  // What if *only* now the eof / fail bit is set?

  /* Use `buffer` */
}

다음과 같이 올바르게 작성할 수 있습니다.

while (!f.eof()) 
{  
  f >> buffer >> std::ws;

  if (f.fail())
    break;

  /* Use `buffer` */
}

그러나

while (f >> buffer)
{
  /* Use `buffer` */
}

더 간단하고 오류가 발생하지 않습니다.

추가 참고 문헌 :

  • std::ws : 입력 스트림에서 선행 공백을 제거합니다.
  • std::basic_ios::fail : 연결된 스트림에서 오류가 발생하면 true 반환 true .

비표준 로켈 설정으로 파일 작성하기

다른 로케일 설정을 사용하여 파일을 기본으로 작성해야하는 경우 std::localestd::basic_ios::imbue() 를 사용하여 특정 파일 스트림에 대해 수행 할 수 있습니다.

사용 지침 :

  • 파일을 열기 전에 항상 스트림에 로컬을 적용해야합니다.
  • 스트림이 채워지면 로케일을 변경하면 안됩니다.

제한 사유 : 현재 로켈이 상태에 종속적이지 않거나 파일의 시작을 가리 키지 않으면 로켈이있는 파일 스트림을 임베드하는 동작이 정의되지 않았습니다.

UTF-8 스트림 (및 기타)은 상태에 독립적입니다. 또한 UTF-8 로켈이있는 파일 스트림이 열리면 파일에서 BOM 마커를 읽거나 시도 할 수 있습니다. 그래서 파일을 열면 파일에서 문자를 읽을 수 있으며 처음에는 나오지 않습니다.

#include <iostream>
#include <fstream>
#include <locale>

int main()
{
  std::cout << "User-preferred locale setting is "
            << std::locale("").name().c_str() << std::endl;

  // Write a floating-point value using the user's preferred locale.
  std::ofstream ofs1;
  ofs1.imbue(std::locale(""));
  ofs1.open("file1.txt");
  ofs1 << 78123.456 << std::endl;

  // Use a specific locale (names are system-dependent)
  std::ofstream ofs2;
  ofs2.imbue(std::locale("en_US.UTF-8"));
  ofs2.open("file2.txt");
  ofs2 << 78123.456 << std::endl;

  // Switch to the classic "C" locale
  std::ofstream ofs3;
  ofs3.imbue(std::locale::classic());
  ofs3.open("file3.txt");
  ofs3 << 78123.456 << std::endl;
}

명시 적으로 클래식 "C"로켈로 전환하는 것은 프로그램이 다른 기본 로케일을 사용하고 파일 읽기 및 쓰기에 대한 고정 표준을 보장하려는 경우에 유용합니다. "C"선호 로케일을 사용하면이 예제는

78,123.456
78,123.456
78123.456

예를 들어 기본 로케일이 독일어이고 다른 숫자 형식을 사용하는 경우이 예제에서는

78 123,456
78,123.456
78123.456

(첫 번째 줄의 10 진수 쉼표를 참고하십시오).



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow