CodeGym /Các khóa học /C# SELF /Ghi file văn bản: StreamWr...

Ghi file văn bản: StreamWriter

C# SELF
Mức độ , Bài học
Có sẵn

1. Giới thiệu

StreamWriter — là một trong những class quan trọng nhất của namespace System.IO trong .NET, dùng để ghi dữ liệu text vào file qua stream một cách tiện lợi.

Tại sao không nên làm việc trực tiếp với FileStream?
FileStream chỉ làm việc với byte thôi. Nếu bạn muốn ghi string qua nó, bạn phải tự chuyển đổi text sang byte và tự lo vụ encoding (làm xong chắc chắn bạn sẽ muốn quay lại với StreamWriter).
StreamWriter thì lo hết mấy chuyện đó cho bạn: bạn đưa string cho nó — nó tự ghi byte đúng vào file.

Ưu điểm chính:

  • Dễ dàng ghi string mà không cần nghĩ về chuyển đổi sang byte.
  • Có method để ghi text theo từng dòng.
  • Quản lý buffer và encoding (và mình sẽ chỉ bạn cách dùng luôn).

Ví dụ đơn giản nhất

using System.IO;

string path = "output.txt";

using (StreamWriter writer = new StreamWriter(path))
{
    writer.WriteLine("Xin chào, thế giới!");
    writer.WriteLine("Đây là dòng thứ hai.");
}
// Sau khi ra khỏi dấu ngoặc nhọn của using, StreamWriter chắc chắn sẽ giải phóng file.

Chuyện gì đang xảy ra ở đây?

  • Mở file để ghi (nếu file chưa có — nó sẽ được tạo mới).
  • Mỗi string được ghi thành một dòng riêng trong file (method WriteLine).
  • Sau khối using file sẽ tự động đóng lại, kể cả khi có lỗi xảy ra.

Nếu bạn mở file output.txt sau khi chạy chương trình này, bạn sẽ thấy hai dòng text, đúng như mong đợi.

Lưu ý quan trọng

Nếu file đã tồn tại, nó sẽ bị ghi đè từ đầu! Mọi thứ bên trong sẽ biến mất. Nên cẩn thận: đừng lưu luận văn hay hóa đơn duy nhất của bạn vào mấy file kiểu này nhé.

2. Ghi dữ liệu vào stream

Các method chính của StreamWriter

Method Mô tả
Write(string)
Ghi string mà không xuống dòng
WriteLine(string)
Ghi string và tự động xuống dòng
Flush()
Ép ghi buffer vào file (hiếm khi phải tự gọi)
Close()
/
Dispose()
Đóng stream và giải phóng resource (dùng using là nó tự làm)
BaseStream
Lấy stream gốc (ví dụ, FileStream)

Method Write()

Ghi dữ liệu mà không xuống dòng mới. Tất cả những gì bạn ghi tiếp theo sẽ nằm trên cùng một dòng trong file.

Method WriteLine()

Ghi dữ liệu và tự động thêm ký tự kết thúc dòng (\r\n trên Windows, \n trên các hệ Unix-like).
Kiểu như bạn bấm Enter sau mỗi lần ghi.

Write() vs WriteLine(): minh họa sự khác biệt

using (var writer = new StreamWriter("example.txt"))
{
    writer.Write("Đoạn đầu ");
    writer.Write("của đoạn văn. ");
    writer.WriteLine("Ghi tiếp dòng, Enter!");
    writer.Write("Đoạn thứ hai.");
}

Giờ file example.txt sẽ trông như này:

Đoạn đầu của đoạn văn. Ghi tiếp dòng, Enter!
Đoạn thứ hai.

3. Làm việc với encoding

Mặc định khi tạo StreamWriter mà không truyền tham số, nó sẽ dùng encoding UTF-8 có BOM (Byte Order Mark).

Thực tế thì như vậy là tiện và hiện đại, nhưng đôi khi bạn cần chỉ định encoding rõ ràng — ví dụ để tương thích với phần mềm cũ hoặc dữ liệu nhập khẩu.

Làm sao chỉ định encoding?

// Ghi file với encoding Windows-1251 (chữ Cyrillic cho hệ cũ)
using (var writer = new StreamWriter("cyrillic.txt", false, System.Text.Encoding.GetEncoding("windows-1251")))
{
    writer.WriteLine("Xin chào, thế giới Cyrillic!");
}

Lưu ý quan trọng:
Encoding phải được hệ thống hỗ trợ. Nếu không chắc — cứ dùng UTF-8.

4. Tham số bổ sung của constructor

Cùng xem bên trong StreamWriter có gì:

public StreamWriter(
    string path,              // đường dẫn file
    bool append = false,      // ghi thêm vào cuối file?
    Encoding encoding = null, // encoding
    int bufferSize = 1024     // kích thước buffer, byte
)
  • append — nếu false (mặc định), file sẽ bị ghi đè. Nếu true, ghi thêm vào cuối file.
  • encoding — encoding sử dụng.
  • bufferSize — kích thước buffer nội bộ để tăng tốc khi làm với dữ liệu lớn.

Ví dụ: ghi thêm vào cùng một file

// File sẽ được ghi thêm, không bị ghi đè
using (var writer = new StreamWriter("log.txt", append: true))
{
    writer.WriteLine(DateTime.Now + " -- Sự kiện mới");
}

5. Một số lưu ý hữu ích

Chuyện gì xảy ra khi ghi thêm và ghi đè

Chế độ Làm gì? Kết quả trong file
append: false Ghi đè toàn bộ Dữ liệu cũ bị xóa
append: true Thêm dòng mới vào cuối Dòng cũ vẫn giữ nguyên

Mẹo nhỏ: Chế độ ghi thêm (append: true) — cực kỳ hợp để ghi log, khi bạn muốn lưu lịch sử sự kiện.

StreamWriter và dữ liệu lớn

  • StreamWriter ghi có buffer: dữ liệu thật sẽ vào file hơi trễ so với lúc bạn gọi WriteLine. Nhưng sau khi đóng stream (hoặc gọi Flush()) thì chắc chắn sẽ có trên ổ đĩa.
  • Ghi bằng WriteLine rất hiệu quả cho xuất từng dòng. Với format phức tạp (JSON, CSV hoặc XML) thì nên dùng thư viện chuyên dụng, hoặc tự cẩn thận escape ký tự đặc biệt (ví dụ, dấu phẩy hoặc dấu nháy).

Cách kết thúc ghi và giải phóng resource đúng chuẩn

Cách chuẩn: luôn dùng using!
Như vậy bạn đảm bảo file sẽ được đóng, kể cả khi có exception (ví dụ, ổ đĩa đầy bất ngờ hoặc file bị chương trình khác khóa).


flowchart TD
    A[Tạo StreamWriter] --> B[Làm việc với file]
    B --> C{Có exception?}
    C -- Có --> D[Gọi Dispose]
    C -- Không --> D
    D --> E[File chắc chắn được đóng]
Sơ đồ: using đảm bảo file được đóng kể cả khi có lỗi

6. Ví dụ thực tế

Giả sử trong khóa học này bạn có một mini-program quản lý sách. Thêm chức năng ghi sách mới vào file nhé.

Ví dụ: Lưu sách mới vào file riêng

using System;
using System.IO;

class Program
{
    static void Main()
    {
        Console.WriteLine("Nhập tên sách:");
        string bookTitle = Console.ReadLine();

        Console.WriteLine("Nhập tác giả:");
        string author = Console.ReadLine();

        string path = "books.txt";
        using (var writer = new StreamWriter(path, append: true))
        {
            writer.WriteLine($"{bookTitle};{author}");
            // Định dạng CSV: mỗi dòng là một cuốn sách, phân cách bằng dấu chấm phẩy
        }

        Console.WriteLine("Sách đã được lưu vào file!");
    }
}

Thử thêm vài cuốn sách liên tiếp xem. Trong file books.txt các dòng sẽ được thêm vào mà không xóa dòng cũ. Kiểu này rất tiện cho việc ghi log hoặc nhật ký — ví dụ cho hệ thống audit tương lai của bạn khi bạn lên trình Enterprise developer.

7. Làm sao tránh lỗi phổ biến khi ghi file

Phần lớn newbie hay gặp mấy lỗi này:

File chưa đóng và bị process khác khóa. Lý do — quên dùng using.

Ghi nhiều dòng mà file vẫn trống: quên gọi Flush() hoặc đóng stream (dùng using thì tự động rồi).

Vô tình ghi đè file thay vì ghi thêm — quên truyền append: true.

Lỗi encoding: file mở ra toàn ký tự lạ trong Notepad — chọn sai encoding hoặc phần mềm khác không hỗ trợ UTF-8.

Exception UnauthorizedAccessException hoặc DirectoryNotFoundException: chương trình cố lưu file vào chỗ không có quyền, hoặc thư mục không tồn tại. Kiểm tra lại đường dẫn và quyền truy cập nhé.

Lỗi "file is used by another process": bạn mở file để ghi mà chưa đóng, hoặc có process khác cũng đang ghi vào đó.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION