在.NET基础类库(BCL)中,有许多类提供了创建和操作文件及目录的方法。本文将介绍一些基本的文件操作,由于这些类提供的功能非常广泛,本文只演示一些非常常用的用法。读者可以自行探索其他功能。本文的初版使用了C#示例,但在这一版中,使用了托管C++。文章包含多种技术,不是自上而下的教程形式。欢迎读者提出更多想要在文章中看到的技巧或技术。
可以使用System.IO.FileInfo类来获取文件信息,该类提供了多种实例方法用于执行各种文件操作。以下是一个简短的示例,将获取Windows记事本的一些信息。使用了原生API调用来获取Windows目录,因为不知道如何使用.NET来实现。如果有人能告诉如何使用.NET来实现,将不胜感激。Show是一个自定义函数,用于输出带填充的文本。
void FileInfoDemo() {
TCHAR WinPath[MAX_PATH+1];
GetWindowsDirectory(WinPath, MAX_PATH+1);
String* NotepadPath = WinPath;
NotepadPath = String::Concat(NotepadPath, "\\notepad.exe");
FileInfo* finfo = new FileInfo(NotepadPath);
Show("Name", finfo->Name);
Show("Directory", finfo->Directory);
Show("Extension", finfo->Extension);
Show("Length", __box(finfo->Length));
Show("FullName", finfo->FullName);
Show("CreationTime ", finfo->CreationTime.ToString());
}
基本上,只需要在构造函数中指定文件的路径。现在可以使用一些非常有用的属性来查询有关文件的信息。FileInfo类有返回各种流读取器和写入器的方法,但后面的文章中将介绍更好的读写文件的方法。
使用DirectoryInfo类来枚举特定文件夹中的子目录和文件。它有两个方法,GetFiles和GetDirectories,前者返回FileInfo数组,后者返回DirectoryInfo数组。
void DirectoryInfoDemo1() {
DirectoryInfo* dinfo = new DirectoryInfo("C:\\");
DirectoryInfo* subdirs[] = dinfo->GetDirectories();
Console::WriteLine("Sub-Directories for C:\\");
Console::WriteLine("-----------------------");
for (int i = 0; i < subdirs->Length; i++)
Show((i+1).ToString(), subdirs->Item[i]);
}
void DirectoryInfoDemo2() {
DirectoryInfo* dinfo = new DirectoryInfo("C:\\");
FileInfo* filesindir[] = dinfo->GetFiles();
Console::WriteLine("Files under C:\\");
Console::WriteLine("---------------");
for (int i = 0; i < filesindir->Length; i++)
Show((i+1).ToString(), filesindir->Item[i]);
}
通过这两个示例,可以遍历C盘根目录下的子目录和文件。
可以使用BinaryReader和BinaryWriter类来读写二进制文件。为了好玩,让创建一个简单的16位COM格式的可执行文件。可以用4行16位汇编语言来编写它,如下所示。它简单地调用中断21h功能02h,输出DL中保存的字符。也给出了这些汇编语句的机器代码等价物。所以所做的就是创建一个包含这8个字节的数组,然后使用BinaryWriter类将数据写入文件。现在实际上可以运行这个文件,它将在屏幕上打印一个'A'。
void BinaryWriterDemo() {
Console::WriteLine("Creating C:\\nish.com");
Console::WriteLine("This will output an 'A' to the console");
FileStream* fs = new FileStream("C:\\nish.com", FileMode::Create, FileAccess::Write);
BinaryWriter* bw = new BinaryWriter(fs);
unsigned char SomeBinaryData __gc[] = {0xb4, 0x02, 0xb2, 0x41, 0xcd, 0x21, 0xcd, 0x20};
bw->Write(SomeBinaryData);
bw->Flush();
bw->Close();
}
现在将展示如何使用BinaryReader类来读取上面创建的二进制文件,并显示文件中的字节。
void BinaryReaderDemo() {
Console::WriteLine("Reading C:\\nish.com");
FileStream* fs = new FileStream("C:\\nish.com", FileMode::Open, FileAccess::Read);
BinaryReader* br = new BinaryReader(fs);
unsigned char c;
while (br->PeekChar() != -1) {
c = br->ReadByte();
Console::Write("{0:X2} ", __box(c));
}
br->Close();
Console::WriteLine();
}
BinaryReader.ReadByte方法的一个小问题是它会抛出EndOfStreamException异常,而不是使用特殊返回值来指示。为了避免处理异常并采用旧的C风格方式,这可能是好事也可能是坏事,使用了PeekChar,它将返回下一个字节但不会移动文件指针。
有多种方式可以读写文本文件,但这里将展示如何使用StreamWriter和StreamReader类。它们让想起了MFC的CStdioFile类。StreamReader类有一个ReadLine方法,StreamWriter类有一个WriteLine方法,这两个方法的行为与CStdioFile::ReadString和CStdioFile::WriteString方法完全相同。这两个类都有构造函数,接受文件路径作为参数,因此不需要像使用BinaryReader和BinaryWriter类那样先创建FileStream对象。
void StreamWriterDemo() {
Console::WriteLine("Creating C:\\TempFile.txt");
StreamWriter *sw = new StreamWriter("C:\\TempFile.txt");
sw->WriteLine("This is the first line");
sw->WriteLine("This is the second line");
sw->WriteLine("This is the third/last line");
sw->Close();
}
void StreamReaderDemo() {
Console::WriteLine("Reading C:\\TempFile.txt");
Console::WriteLine("-----------------------");
StreamReader* sr = new StreamReader("C:\\TempFile.txt");
String* s;
while ((s = sr->ReadLine()))
Console::WriteLine(s);
sr->Close();
}
通过这两个示例,可以创建和读取文本文件。
他们考虑了一切,不是吗?StringReader是从TextReader派生的类,允许直接从字符串中读取。还有一个对应的StringWriter类。不太确定它的用途,但想有人会找到它的一些用途。也许它对于解析简单的字符串很有用。
void StringReaderDemo(String* s) {
Console::WriteLine("Reading the string [{0}]", s);
StringReader* sr = new StringReader(s);
int c;
while ((c = sr->Read()) != -1)
Console::Write("{0} ", __box(Convert::ToChar(c)));
Console::WriteLine();
sr->Close();
}