Hướng dẫn sử dụng Stream - luồng vào ra nhị phân trong C#
1. Tổng quan về Stream
Stream là một class nó mô phỏng một dòng các byte được sắp hàng một cách liên tiếp nhau. Chẳng hạn như việc truyền tải dữ liệu trên mạng các dữ liệu truyền đi là dòng các byte liên tiếp nhau từ byte đầu tiên cho tới các byte cuối cùng.
Stream là một class cơ sở, các luồng (stream) khác mở rộng từ class này. Có một vài class đã được xây dựng sẵn trong C#, chúng mở rộng từ lớp Stream cho các mục đích khác nhau, chẳng han:
Class | Mô tả |
BufferedStream | Một luồng tiện ích, nó bao bọc (wrap) một luồng khác giúp nâng cao hiệu năng. |
FileStream | Luồng sử dụng để đọc ghi dữ liệu vào file. |
MemoryStream | Luồng làm việc với các dữ liệu trên bộ nhớ. |
UnmanagedMemoryStream | |
IsolatedStorageFileStream | |
PipeStream | |
NetworkStream | |
CryptoStream | Luồng đọc ghi dữ liệu được mật mã hóa. |
DeflateStream | |
GZipStream |
Stream là một lớp trừu tượng, tự nó không thể khởi tạo một đối tượng, bạn có thể khởi tạo một đối tượng Stream từ các phương thức khởi tạo (Constructor) của class con. Lớp Stream cung cấp các phương thức cơ bản làm việc với luồng dữ liệu, cụ thể là các phương thức đọc ghi một byte hoặc một mảng các byte..
Tùy thuộc vào luồng, có những luồng hỗ trợ cả đọc và ghi, và cả tìm kiếm (seek) bằng cách di chuyển con trỏ trên luồng, và ghi đọc dữ liệu tại vị trí con trỏ.
Các thuộc tính (property) của Stream:
Thuộc tính | Mô tả |
CanRead | Thuộc tính cho biết luồng này có hỗ trợ đọc không. |
CanSeek | Thuộc tính cho biết luồng này có hỗ trợ tìm kiếm (seek) hay không |
CanWrite | Thuộc tính cho biết luồng này có hỗ trợ ghi hay không |
Length | Trả về độ dài của luồng (Số bytes) |
Position | Vị trí hiện tại của con trỏ trên luồng. |
Các phương thức của Stream:
2. Ví dụ cơ bản Stream
Với Stream bạn có thể ghi từng byte hoặc ghi một mảng các byte vào luồng (stream). Và khi đọc bạn có thể đọc từng byte hoặc đọc nhiều byte và gán vào một mảng tạm.
Một byte là 8 bit, trong đó một bit là 0 hoặc 1. Như vậy 1 byte tương ứng với một số từ 0 tới 255 (2^8 - 1).
Ví dụ luồng ghi dữ liệu
Và bây giờ hãy bắt đầu với một ví dụ đơn giản, tạo một Stream ghi dữ liệu vào File. Bạn có thể ghi từng byte vào stream hoặc ghi một mảng các byte vào Stream.
StreamWriteDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace CSharpStreamsTutorial
{
class StreamWriteDemo
{
public static void Main(string[] args)
{
string path = @"C:\temp\MyTest.txt";
// Tạo thư mục cha.
Directory.CreateDirectory(@"C:\temp");
// Tạo một đối tượng Stream thông qua constructor của FileStream.
// FileMode.Create: Tạo file mới để ghi, nếu file đã tồn tại ghi đè file này.
Stream writingStream = new FileStream(path, FileMode.Create);
try
{
// Một mảng các byte (1byte < 2^8).
// Mảng này tương ứng với: {'H','e','l','l','o',' ','W','o','r','l','d'}.
byte[] bytes = new byte[] { 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100 };
if (writingStream.CanWrite)
{
writingStream.Write(bytes, 0, bytes.Length);
// Ghi thêm một byte (33 = '!')
writingStream.WriteByte(33);
}
}
catch (Exception e)
{
Console.WriteLine("Error:" + e);
}
finally
{
// Đóng Stream, giải phóng tài nguyên.
writingStream.Close();
}
Console.ReadLine();
}
}
}
Chạy ví dụ:
Chú ý: Trong bảng mã ký tự CSII, mỗi ký tự CSII tương ứng với một con số < 256.
Ký tự Giá trị Ký tự Giá trị H 72 W 87 e 101 r 114 l 108 d 100 o 111 32 ! 33 Bạn có thể tham khảo thêm về bảng mã ASCII tại:
Ví dụ luồng đọc dữ liệu
Ví dụ ở trên bạn đã ghi dữ liệu vào file C:\temp\MyTest.txt, bây giờ bạn có thể viết một stream đọc dữ liệu từ file đó.
StreamReadDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace CSharpStreamsTutorial
{
class StreamReadDemo
{
public static void Main(string[] args)
{
String path = @"C:\temp\MyTest.txt";
if (!File.Exists(path))
{
Console.WriteLine("File " + path + " does not exists!");
return;
}
// Tạo một đối tượng Stream thông qua Constructor của lớp FileStream.
// FileMode.Open: Mở file để đọc.
using (Stream readingStream = new FileStream(path, FileMode.Open))
{
byte[] temp = new byte[10];
UTF8Encoding encoding = new UTF8Encoding(true);
int len = 0;
// Đọc các phần tử trên Stream và gán vào các phần tử của mảng 'temp'.
// (Gán vào các vị trí bắt đầu từ 0, mỗi lần đọc tối đa 'temp.Length' phần tử)
// Đồng thời trả về số byte vừa đọc được.
while ((len = readingStream.Read(temp, 0, temp.Length)) > 0)
{
// Chuyển thành chuỗi (String).
// ('len' phần tử bắt đầu từ vị trí 0).
String s = encoding.GetString(temp, 0, len);
Console.WriteLine(s);
}
}
Console.ReadLine();
}
}
}
Chạy ví dụ:
3. FileStream
FileStream là một lớp mở rộng từ lớp Stream, FileStream được sử dụng để đọc và ghi dữ liệu vào file, nó được thừa kế các thuộc tính (property), phương thức từ Stream, đồng thời có thêm các chức năng dành riêng cho đọc ghi dữ liệu vào file.
Có một vài chế độ đọc ghi dữ liệu vào file:
FileMode | Mô tả |
Append | Mở file nếu nó đã tồn tại, di chuyển con trỏ về cuối tập tin để nó thể ghi nối tiếp vào file, nếu file không tồn tại nó sẽ được tạo ra. |
Create | Nói với hệ điều hành tạo một tập tin mới. Nếu tập tin đã tồn tại, nó sẽ được ghi đè. |
CreateNew | Nói với hệ điều hành tạo ra một file mới. Nếu file đã tồn tại ngoại lệ IOException sẽ được ném ra. Chế độ này yêu cầu phải có quyền FileIOPermissionAccess.Write |
Open | Nói với hệ điều hành để mở một file đã tồn tại. Một ngoại lệ System.IO.FileNotFoundException sẽ được ném ra nếu file không tồn tại. |
OpenOrCreate | Nói với hệ điều hảnh nên mở một tập tin nếu nó tồn tại; nếu không, một tập tin mới sẽ được tạo ra. |
Truncate | Nói với hệ điều hành mở tập tin khi nó tồn tại. Và khi file được mở, nó sẽ bị cắt hết nội dung trở về 0 byte. |
Ví dụ với FileMode:
FileStreamFileModeDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace CSharpStreamsTutorial
{
class FileStreamFileModeDemo
{
public static void Main(string[] args)
{
String path = @"C:\temp\MyTest.txt";
if (!File.Exists(path))
{
Console.WriteLine("File " + path + " does not exists!");
// Đảm bảo rằng thư mục cha tồn tại.
Directory.CreateDirectory(@"C:\temp");
}
// Tạo ra một FileStream để ghi dữ liệu.
// (FileMode.Append: Mở file ra để ghi tiếp vào phía cuối của file,
// nếu file không tồn tại sẽ tạo mới).
using (FileStream writeFileStream = new FileStream(path, FileMode.Append) )
{
string s = "\nHello every body!";
// Chuyển một chuỗi thành mảng các byte theo mã hóa UTF8.
byte[] bytes = Encoding.UTF8.GetBytes(s);
// Ghi các byte xuống file.
writeFileStream.Write(bytes, 0, bytes.Length);
}
Console.WriteLine("Finish!");
Console.ReadLine();
}
}
}
Chạy ví dụ:
Với FileMode.Append dữ liệu sẽ được nối thêm vào file, nếu file đó đã tồn tại:
Phương thức khởi tạo (Constructor):
Class FileStream có 11 constructor (Không tính các constructor bị lỗi thời) dùng để khởi tạo một đối tượng FileStream:
FileStream Constructors
FileStream(SafeFileHandle, FileAccess)
FileStream(SafeFileHandle, FileAccess, Int32)
FileStream(SafeFileHandle, FileAccess, Int32, Boolean)
FileStream(String, FileMode)
FileStream(String, FileMode, FileAccess)
FileStream(String, FileMode, FileAccess, FileShare)
FileStream(String, FileMode, FileAccess, FileShare, Int32)
FileStream(String, FileMode, FileAccess, FileShare, Int32, Boolean)
FileStream(String, FileMode, FileAccess, FileShare, Int32, FileOptions)
FileStream(String, FileMode, FileSystemRights, FileShare, Int32, FileOptions)
FileStream(String, FileMode, FileSystemRights, FileShare, Int32, FileOptions, FileSecurity)
Tuy nhiên bạn cũng có các cách khác để tạo đối tượng FileStream, chẳng hạn thông qua FileInfo, đây là là class đại diện cho một file trong hệ thống.
Phương thức của FileInfo trả về FileStream. | Mô tả |
Create() | Bởi mặc định, tất cả các quyền đọc ghi file mới này sẽ gán cho tất cả các users. |
Open(FileMode) | Mở file với chế độ được chỉ định. |
Open(FileMode, FileAccess) | Mở file với chỉ định chế độ đọc, ghi, hoặc quyền đọc ghi. |
Open(FileMode, FileAccess, FileShare) | Mở file với chỉ định chế độ đọc, ghi, hoặc quyền đọc ghi, và các lựa chọn chia sẻ. |
OpenWrite() | Tạo ra một FileStream chỉ để ghi dữ liệu. |
OpenRead() | Tạo ra FileStream chỉ để đọc dữ liệu. |
Ví dụ tạo FileStream từ FileInfo:
FileStreamFileInfoDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace CSharpStreamsTutorial
{
class FileStreamFileInfoDemo
{
public static void Main(string[] args)
{
FileInfo afile = new FileInfo(@"C:\temp\MyTest.txt");
if (afile.Exists)
{
Console.WriteLine("File does not exist!");
Console.Read();
return;
}
// Mở file và cắt cụt (Truncate) hết dữ liệu hiện tại.
using (FileStream stream = afile.Open(FileMode.Truncate))
{
String s = "New text";
byte[] bytes = Encoding.UTF8.GetBytes(s);
stream.Write(bytes, 0, bytes.Length);
}
Console.WriteLine("Finished!");
Console.Read();
}
}
}
4. BufferedStream
BufferedStream là một lớp mở rộng từ lớp Stream, nó là một luồng (stream) bộ đệm bao lấy (wrap) một stream khác, giúp nâng cao hiệu quả đọc ghi dữ liệu.
BufferedStream chỉ có 2 phương thức khởi tạo (Constructor), nó bao lấy một stream khác.
Phương thức khởi tạo (Constructor) | Mô tả |
BufferedStream(Stream) | Khởi tạo một đối tượng BufferedStream với kích thước bộ đệm mặc định là 4096 bytes. |
BufferedStream(Stream, Int32) | Khởi tạo một đối tượng BufferedStream với kích thước bộ đệm được chỉ định. |
Tôi đưa ra một tình huống, bạn tạo ra một luồng bộ đệm (BufferedStream) bao lấy FileStream, với mục đích ghi dữ liệu xuống file. Các dữ liệu ghi vào luồng bộ đệm tạm thời sẽ nằm trên bộ nhớ, và khi bộ đệm đầy, dữ liệu tự động được đẩy (Flush) xuống file, bạn có thể chủ động đẩy dữ liệu xuống file bằng cách sử dụng phương thức Flush(). Sử dụng BufferedStream trong trường hợp này giúp giảm số lần phải ghi xuống ổ đĩa, và vì vậy nó làm tăng hiệu suất của chương trình.
Ví dụ dưới đây một BufferedStream bao lấy một luồng ghi dữ liệu vào file:
BufferedStreamWriteFileDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace CSharpStreamsTutorial
{
class BufferedStreamWriteFileDemo
{
public static void Main(string[] args)
{
String fileName = @"C:\temp\MyFile.txt";
FileInfo file = new FileInfo(fileName);
// Đảm bảo thư mục tồn tại.
file.Directory.Create();
// Tạo file mới, nếu nó đã tồn tại nó sẽ bị ghi đè.
// Trả về một đối tượng FileStream.
using (FileStream fileStream = file.Create())
{
// Tạo một đối tượng BufferedStream bao lấy FileStream.
// (Chỉ định bộ đệm (buffer) có sức chứa 10000 bytes).
using (BufferedStream bs = new BufferedStream(fileStream, 10000))
{
int index = 0;
for (index = 1; index < 2000; index++)
{
String s = "This is line " + index + "\n";
byte[] bytes = Encoding.UTF8.GetBytes(s);
// Ghi vào bộ đệm (buffer),
// khi bộ đệm đầy nó sẽ tự động đẩy dữ liệu xuống file.
bs.Write(bytes, 0, bytes.Length);
}
// Đẩy các dữ liệu còn lại trên bộ đệm xuống file.
bs.Flush();
}
}
Console.WriteLine("Finished!");
Console.Read();
}
}
}
Kết quả chạy ví dụ:
5. MemoryStream
MemoryStream là một lớp mở rộng trực tiếp từ lớp Stream, nó là luồng (stream) mà dữ liệu được lưu trữ (store) trên bộ nhớ.
Về bản chất MemoryStream là một đối tượng nó quản lý một bộ đệm (buffer) là một mảng các byte, khi các byte được ghi vào luồng này nó sẽ tự động được gán vào các vị trí tiếp theo tính từ vị trí hiện tại của con trỏ trên mảng. Khi bộ đệm đầy một mảng mới có kích thước lớn hơn được tạo ra, và copy các dữ liệu từ mảng cũ sang.
Constructor:
MemoryStream() |
MemoryStream(Byte[] buffer) |
MemoryStream(Byte[] buffer, Boolean writable) |
MemoryStream(Byte[] buffer, Int32 index, Int32 count, Boolean writable) |
MemoryStream(Byte[] buffer, Int32 index, Int32 count, Boolean, Boolean publiclyVisible) |
MemoryStream(Byte[], Int32, Int32, Boolean, Boolean) |
MemoryStream(Int32 capacity) |
Ví dụ:
MemoryStreamDemo.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace CSharpStreamsTutorial
{
class MemoryStreamDemo
{
static void Main()
{
// Tạo một đối tượng MemoryStream có dung lượng 100 bytes.
MemoryStream memoryStream = new MemoryStream(100);
byte[] javaBytes = Encoding.UTF8.GetBytes("Java");
byte[] csharpBytes = Encoding.UTF8.GetBytes("CSharp");
// Ghi các byte vào memoryStream (luồng bộ nhớ).
memoryStream.Write(javaBytes, 0, javaBytes.Length);
memoryStream.Write(csharpBytes, 0, csharpBytes.Length);
// Ghi ra sức chứa và độ dài của Stream.
// ==> Capacity: 100, Length: 10.
Console.WriteLine("Capacity: {0} , Length: {1}",
memoryStream.Capacity.ToString(),
memoryStream.Length.ToString());
// Lúc này vị trí con trỏ (cursor) đang đứng ở sau ký tự 'p'.
// ==> 10.
Console.WriteLine("Position: "+ memoryStream.Position);
// Di chuyển lùi con trỏ lại 6 byte, so với vị trí hiện tại.
memoryStream.Seek(-6, SeekOrigin.Current);
// Lúc này vị trí con trỏ đang đứng ở sau ký tự 'a' và trước 'C'.
// ==> 4.
Console.WriteLine("Position: " + memoryStream.Position);
byte[] vsBytes = Encoding.UTF8.GetBytes(" vs ");
// Ghi dữ liệu vào memoryStream (Luồng bộ nhớ).
memoryStream.Write(vsBytes, 0, vsBytes.Length);
byte[] allBytes = memoryStream.GetBuffer();
string data = Encoding.UTF8.GetString(allBytes);
// ==> Java vs rp
Console.WriteLine(data);
Console.WriteLine("Finish!");
Console.Read();
}
}
}
Chạy ví dụ:
6. UnmanagedMemoryStream
Sử dụng UnmanagedMemoryStream cho phép bạn đọc các luồng dữ liệu không được quản lý mà không cần sao chép tất cả chúng lên quản lý ở bộ nhớ Heap trước khi sử dụng. Nó giúp bạn tiết kiệm bộ nhớ nếu bạn đang phải đối phó với rất nhiều dữ liệu.
Lưu ý rằng có một giới hạn 2GB đối với MemoryStream vì vậy bạn phải sử dụng các UnmanagedMemoryStream nếu bạn vượt quá giới hạn này.
Lưu ý rằng có một giới hạn 2GB đối với MemoryStream vì vậy bạn phải sử dụng các UnmanagedMemoryStream nếu bạn vượt quá giới hạn này.
Tôi đưa ra một tình huống: Có các dữ liệu rời rạc nằm sẵn trên bộ nhớ. Và bạn có thể tập hợp chúng lại để quản lý bởi UnmanagedMemoryStream bằng cách quản lý các con trỏ (pointer) của các dữ liệu rời rạc nói trên, thay vì bạn copy chúng lên luồng (stream) để quản lý.
Constructor:
UnmanagedMemoryStream() |
UnmanagedMemoryStream(Byte* pointer, Int64 length) |
UnmanagedMemoryStream(Byte* pointer, Int64 length, Int64 capacity, FileAccess access) |
UnmanagedMemoryStream(SafeBuffer buffer, Int64 offset, Int64 length) |
UnmanagedMemoryStream(SafeBuffer buffer, Int64 offset, Int64 length, FileAccess access) |
- TODO
7. CryptoStream
CryptoStream là một lớp, sử dụng cho việc mật mã hóa luồng dữ liệu.
Hình ảnh minh họa dưới đây luồng CryptoStream bao lấy một luồng khác (chẳng hạn là luồng ghi file), khi bạn ghi dữ các byte lên CryptoStream các byte này sẽ bị mật mã hóa thành các byte khác trước khi đẩy sang luồng ghi vào file. Lúc này nội dung của file đã được mật mã hóa.
Chú ý rằng bạn có thể lựa chọn một thuật toán mật mã hóa khi tạo đối tượng CryptoStream.
Chú ý rằng bạn có thể lựa chọn một thuật toán mật mã hóa khi tạo đối tượng CryptoStream.
Trong một tình huống ngược lại, một luồng CryptoStream bao lấy một luồng đọc file (File mà nội dung đã mã hóa ở trên), các byte trên luồng FileStream là các byte đã được mật mã hóa (encrypt), nó sẽ được giải mật (decrypt) bởi CryptoStream.
Một điều quan trọng bạn cần nhớ rằng, không phải thuật toán mật mã hóa nào cũng có 2 chiều mật mã hóa và giải mật mã hóa.
Hãy xem một ví dụ:
Ở đây tôi sử dụng thuật toán DES để mã hóa và giải mã, bạn cần cung cấp mảng 128 bit nó là chìa khóa bảo mật của bạn.
Ở đây tôi sử dụng thuật toán DES để mã hóa và giải mã, bạn cần cung cấp mảng 128 bit nó là chìa khóa bảo mật của bạn.
DES Algorithm
// Đối tượng cung cấp thuật toán mật mã hóa DES.
DESCryptoServiceProvider provider = new DESCryptoServiceProvider();
// Sử dụng khóa riêng tư (private key) của bạn (Phải là một chuỗi 128bit = 8byte).
// (Tương đương với 8 ký tự ASCII)
provider.Key = ASCIIEncoding.ASCII.GetBytes("1234abcd");
provider.IV = ASCIIEncoding.ASCII.GetBytes("12345678");
// Bộ mật mã hóa (Encryptor).
ICryptoTransform encryptor = provider.CreateEncryptor();
// Bộ giải mật mã hóa (Decrytor).
ICryptoTransform decryptor = provider.CreateDecryptor();
Xem ví dụ đầy đủ.
CryptoStreamExample.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Security.Cryptography;
namespace CSharpStreamsTutorial
{
class CryptoStreamExample
{
public static void Main(string[] args)
{
// Cung cấp thuật toán mật mã hóa DES.
DESCryptoServiceProvider provider = new DESCryptoServiceProvider();
// Khóa bảo mật (secret key) (Phải là một chuỗi 128bit = 8byte).
// (Tương đương với 8 ký tự ASCII)
provider.Key = ASCIIEncoding.ASCII.GetBytes("1234abcd");
provider.IV = ASCIIEncoding.ASCII.GetBytes("12345678");
String encryedFile = @"C:\temp\EncryptedFile.txt";
// Một luồng để ghi file.
using (FileStream stream = new FileStream(encryedFile, FileMode.OpenOrCreate, FileAccess.Write))
{
// Đối tượng mật mã hóa (Encryptor).
ICryptoTransform encryptor = provider.CreateEncryptor();
// Tạo một CryptoStream bao lấy FileStream.
// (FileStream trong trường hợp này dùng để ghi dữ liệu vào file).
using (CryptoStream cryptoStream = new CryptoStream(stream,
encryptor, CryptoStreamMode.Write))
{
// Một mảng byte chưa được mật mã hóa.
byte[] data = ASCIIEncoding.ASCII.GetBytes("Bear, I love you. OK?");
// Ghi vào cryptoStream.
cryptoStream.Write(data, 0, data.Length);
}
}
Console.WriteLine("Write to file: " + encryedFile);
// Tiếp theo đọc file đã được mật mã hóa, vừa được tạo ra ở bước trên.
using (FileStream stream = new FileStream(encryedFile, FileMode.Open, FileAccess.Read))
{
// Đối tượng giải mật mã hóa (Decryptor).
ICryptoTransform decryptor = provider.CreateDecryptor();
// Tạo một CryptoStream bao lấy FileStream.
// (FileStream trong trường hợp này dùng để đọc file).
using (CryptoStream cryptoStream = new CryptoStream(stream,
decryptor, CryptoStreamMode.Read))
{
byte[] temp = new byte[1024];
int read=0;
while((read =cryptoStream.Read(temp,0,temp.Length )) >0 )
{
String s= Encoding.UTF8.GetString(temp,0,read);
Console.Write(s);
}
}
}
// Finished
Console.Read();
}
}
}
Chạy ví dụ:
Xem nội dung của file vừa được tạo ra.
Các hướng dẫn lập trình C#
- Thừa kế và đa hình trong C#
- Bắt đầu với C# cần những gì?
- Học nhanh C# cho người mới bắt đầu
- Cài đặt Visual Studio 2013 trên Windows
- Abstract class và Interface trong C#
- Cài đặt Visual Studio 2015 trên Windows
- Nén và giải nén trong C#
- Hướng dẫn lập trình đa luồng trong C#
- Hướng dẫn và ví dụ C# Delegate và Event
- Cài đặt AnkhSVN trên Windows
- Lập trình C# theo nhóm sử dụng Visual Studio và SVN
- Cài đặt .Net Framework
- Access Modifier trong C#
- Hướng dẫn và ví dụ C# String và StringBuilder
- Hướng dẫn và ví dụ C# Property
- Hướng dẫn và ví dụ C# Enum
- Hướng dẫn và ví dụ C# Structure
- Hướng dẫn và ví dụ C# Generics
- Hướng dẫn xử lý ngoại lệ trong C#
- Hướng dẫn và ví dụ Date Time trong C#
- Thao tác với tập tin và thư mục trong C#
- Hướng dẫn sử dụng Stream - luồng vào ra nhị phân trong C#
- Hướng dẫn sử dụng biểu thức chính quy trong C#
- Kết nối cơ sở dữ liệu SQL Server trong C#
- Làm việc với cơ sở dữ liệu SQL Server trong C#
- Kết nối cơ sở dữ liệu MySQL trong C#
- Làm việc với cơ sở dữ liệu MySQL trong C#
- Kết nối cơ sở dữ liệu Oracle trong C# không cần Oracle Client
- Làm việc với cơ sở dữ liệu Oracle trong C#
Show More