首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++/CLI 輸入輸出完整指南:串流、檔案與二進制操作

     編輯:關於C++

前言

在 C++/CLI 中,輸入輸出(I/O)透過 串流 概念統一抽象。資料可為 8 位元組或 16 位元 Unicode 字元,分別對應位元組串流(Stream 系)與字元串流(TextReader/TextWriter 系)。本文系統整理標準 I/O、檔案 I/O、字串 I/O、二進制 I/O、隨機存取、檔案目錄操作等核心主題。

核心類別繼承架構

.NET I/O 類別主要分兩大體系:

  • 位元組串流:Stream → FileStream, MemoryStream, BufferedStream → BinaryReader/BinaryWriter
  • 字元串流:TextReader → StreamReader, StringReader;TextWriter → StreamWriter, StringWriter

此外還有 File, Directory, Path, FileInfo, DirectoryInfo 等輔助類別。

標準串流

程式啟動時自動開啟三個標準串流:

串流屬性型別預設裝置重導向方法
標準輸入Console::InTextReader鍵盤Console::SetIn
標準輸出Console::OutTextWriter螢幕Console::SetOut
標準錯誤Console::ErrorTextWriter螢幕Console::SetError

範例 1:基本字元串流操作


using namespace System;
using namespace System::IO;

void Copy(TextReader^ inStream, TextWriter^ outStream);

int main() {
    TextReader^ inStream = Console::In;
    TextWriter^ outStream = Console::Out;
    
    // 讀寫單一字元(需轉型)
    outStream->Write(static_cast(inStream->Read()));
    outStream->Write(static_cast(inStream->Read()));
    outStream->Flush();
    
    // 陣列緩衝讀寫
    array^ buffer = {L'w', L'x', L'y', L'z'};
    inStream->Read(buffer, 1, 2);  // 讀 2 字元存入 buffer[1], buffer[2]
    outStream->Write(buffer);      // 輸出整個陣列
    
    Copy(inStream, outStream);     // 複製剩餘內容至 EOF
    
    // 格式化輸出
    outStream->Write("{0} * {1} = {2}\n", 10, 5, 10 * 5);
    
    inStream->Close();
    outStream->Close();
}

void Copy(TextReader^ inStream, TextWriter^ outStream) {
    int c;
    while ((c = inStream->Read()) != -1) {
        outStream->Write(static_cast(c));
    }
}

關鍵點:

  • Read() 回傳 int 而非 wchar_t,以便用 -1 表示 EOF
  • Flush() 強制清空緩衝區
  • TextReader/TextWriter 為抽象基類,實際操作多態於衍生類別

檔案 I/O

檔案操作透過 FileStream、StreamReader/StreamWriter 或 File 靜態方法進行。

範例 2:檔案複製與例外處理


using namespace System;
using namespace System::IO;

void Copy(TextReader^ inStream, TextWriter^ outStream);

int main(array^ argv) {
    if (argv->Length != 2) {
        Console::WriteLine("需要兩個參數。");
        Environment::Exit(1);
    }
    
    try {
        // 開啟輸入檔
        FileStream^ inFile = gcnew FileStream(argv[0], FileMode::Open);
        StreamReader^ inStream = gcnew StreamReader(inFile);
        // 等同於:StreamReader^ inStream = File::OpenText(argv[0]);
        
        Console::WriteLine("CanRead={0}, CanWrite={1}", inFile->CanRead, inFile->CanWrite);
        
        // 建立輸出檔
        StreamWriter^ outStream = File::CreateText(argv[1]);
        
        Copy(inStream, outStream);
        outStream->Write("{0} * {1} = {2}\n", 10, 5, 10 * 5);
        
        inStream->Close();
        outStream->Close();
    }
    catch (FileNotFoundException^ ex) {
        Console::WriteLine(ex->Message);
    }
    catch (IOException^ ex) {
        Console::WriteLine(ex);
    }
}

注意: catch 區塊順序很重要——FileNotFoundException 繼承自 IOException,必須先捕獲衍生類別。

字串 I/O

使用 StringReader/StringWriter 對記憶體字串進行串流操作,底層以 StringBuilder 為緩衝區。

範例 3:字串讀寫與 StringBuilder 互動


using namespace System;
using namespace System::IO;
using namespace System::Text;

void Copy(TextReader^ inStream, TextWriter^ outStream);

int main() {
    String^ str = "abcde";
    StringReader^ inStream = gcnew StringReader(str);
    StringWriter^ outStream = gcnew StringWriter();
    
    StringBuilder^ sb = outStream->GetStringBuilder();
    Console::WriteLine("Capacity is {0}", sb->Capacity);  // 預設 16
    
    outStream->Write(static_cast(inStream->Read()));  // 'a'
    outStream->Write('!');                                     // '!'
    outStream->Write(static_cast(inStream->Read()));  // 'b'
    outStream->Flush();
    outStream->Write("Result = {0,4:0.##}", 10.0/3);           // 格式化輸出
    
    Console::WriteLine(outStream);  // 隱式呼叫 ToString()
    Copy(inStream, outStream);      // 複製剩餘 "cde"
    Console::WriteLine(outStream);
    Console::WriteLine(sb);         // 直接輸出 StringBuilder
    
    inStream->Close();
    outStream->Close();
}

輸出:


Capacity is 16
a!bResult = 3.33
a!bResult = 3.33cde
a!bResult = 3.33cde

二進制 I/O

使用 BinaryReader/BinaryWriter 進行型別化的位元組讀寫,支援 bool, char, int, long, float, double, String 等基本型別。

範例 4:二進制資料讀寫


using namespace System;
using namespace System::IO;

int main() {
    // 寫入
    Stream^ fs = File::Create("io04.dat");
    BinaryWriter^ bw = gcnew BinaryWriter(fs);
    bw->Write(true);
    bw->Write(L'A');
    bw->Write(0xabcd);
    bw->Write(0x12345678LL);
    bw->Write(123.456F);
    bw->Write("Hello");
    bw->Close();
    fs->Close();
    
    // 讀取(須按寫入順序)
    fs = File::Open("io04.dat", FileMode::Open);
    BinaryReader^ br = gcnew BinaryReader(fs);
    Console::WriteLine("bool: " + br->ReadBoolean());
    Console::WriteLine("wchar_t: " + br->ReadChar());
    Console::WriteLine("int: " + br->ReadInt32());
    Console::WriteLine("long long: " + br->ReadInt64());
    Console::WriteLine("float: " + br->ReadSingle());
    Console::WriteLine("String: " + br->ReadString());
    br->Close();
    fs->Close();
}

輸出:


bool: True
wchar_t: A
int: 43981
long long: 305419896
float: 123.456
String: Hello

隨機存取 I/O

FileStream 支援 Position 與 Seek() 進行隨機定位,需指定 FileAccess::ReadWrite。

範例 5:隨機讀寫與覆寫


using namespace System;
using namespace System::IO;

int main() {
    Stream^ fs = gcnew FileStream("Io05.dat", FileMode::Create, FileAccess::ReadWrite);
    BinaryWriter^ bw = gcnew BinaryWriter(fs);
    BinaryReader^ br = gcnew BinaryReader(fs);
    
    Console::WriteLine("CanRead={0}, CanWrite={1}, CanSeek={2}", 
        fs->CanRead, fs->CanWrite, fs->CanSeek);
    Console::WriteLine("Position at start: {0}", fs->Position);
    
    bw->Write(true);                    // 位元組 0
    long long pos1 = fs->Position;      // 記住位置 1
    bw->Write(1234);                    // 位元組 1-4
    bw->Write(123.456);                 // 位元組 5-12
    Console::WriteLine("Position at end: {0}", fs->Position);  // 13
    
    fs->Position = pos1;                // 回到位置 1
    bw->Write(5678);                    // 覆寫 1234 為 5678
    
    fs->Position = 0;                   // 回到開頭
    bw->Write(false);                   // 覆寫 true 為 false
    
    fs->Seek(0, SeekOrigin::Begin);
    Console::WriteLine("bool: " + br->ReadBoolean());  // False
    
    fs->Seek(-1, SeekOrigin::Current);  // 回退 1 位元組
    Console::WriteLine("bool: " + br->ReadBoolean());  // 再次讀取 False
    
    Console::WriteLine("int: " + br->ReadInt32());     // 5678
    
    fs->Seek(-8, SeekOrigin::End);      // 從末尾倒退 8 位元組
    Console::WriteLine("double: " + br->ReadDouble()); // 123.456
    
    bw->Close(); br->Close(); fs->Close();
}

輸出:


CanRead is True, CanWrite is True, CanSeek is True
Position at start is 0
Position at end is 13
boolean: False
boolean: False
int: 5678
double: 123.456

警告: 隨機定位至任意位元組可能落在多位元組數值中間,導致讀取無意義資料。

檔案與目錄操作

File, Directory, Path, FileInfo, DirectoryInfo 提供豐富的靜態與實例方法。

範例 6:檔案資訊與操作


using namespace System;
using namespace System::IO;

int main() {
    String^ fName1 = "Io06";
    if (!Path::HasExtension(fName1)) {
        fName1 = Path::ChangeExtension(fName1, ".dat");
    }
    Console::WriteLine("fName1 is {0}", fName1);
    
    StreamWriter^ outStream = File::CreateText(fName1);
    outStream->Write("some text");
    outStream->Close();
    
    String^ fName2 = "Io06Copy.dat";
    Console::WriteLine("File {0} exists: {1}", fName2, File::Exists(fName2));
    File::Copy(fName1, fName2);
    Console::WriteLine("File {0} exists: {1}", fName2, File::Exists(fName2));
    File::Delete(fName2);
    Console::WriteLine("File {0} exists: {1}", fName2, File::Exists(fName2));
    
    FileInfo^ f = gcnew FileInfo(fName1);
    Console::WriteLine("FullName: {0}", f->FullName);
    Console::WriteLine("DirectoryName: {0}", f->DirectoryName);
    Console::WriteLine("Name: {0}", f->Name);
    Console::WriteLine("Attributes: {0}", f->Attributes);
    Console::WriteLine("Length: {0}", f->Length);
    Console::WriteLine("CreationTime: {0}", f->CreationTime);
    Console::WriteLine("LastAccessTime: {0}", f->LastAccessTime);
    Console::WriteLine("LastWriteTime: {0}", f->LastWriteTime);
}

其他進階主題

  • 序列化:持久化物件圖形至串流,支援跨程式、跨執行期壽命的資料交換
  • 非同步 I/O:BeginRead/EndRead, BeginWrite/EndWrite 模式,避免阻塞執行緒(本文不詳細展開)
  • 編碼處理:StreamReader/StreamWriter 可指定 Encoding(UTF-8, Unicode, BigEndianUnicode 等)

總結

需求建議類別
文字檔讀寫StreamReader / StreamWriter
記憶體字串讀寫StringReader / StringWriter
二進制型別資料BinaryReader / BinaryWriter
隨機存取/覆寫FileStream + Position/Seek
檔案/目錄管理File, Directory, Path, FileInfo, DirectoryInfo
標準輸入輸出Console::In, Console::Out, Console::Error
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved