在本文中,我们介绍了C#操作中的输入和输出。C#中的输入和输出是基于流的。
C#流
流是字节序列的抽象,例如文件、输入/输出设备、进程间通信管道或TCP/IP套接字。流从一个字节传输数据指向另一个点。流也能够操纵数据;例如,他们可以压缩或加密数据。在.NET中,System.IO命名空间包含支持读取和写入数据流和文件的类型。
C#在File类中为I/O操作提供高级方法,在StreamReader或StreamWriter类中提供低级方法>.
处理异常
I/O操作容易出错。我们可能会遇到诸如FileNotFoundException或UnauthorizedAccessException之类的异常。与Java不同,C#不强制程序员手动处理异常,是否手动处理异常由程序员决定。如果异常未在try/catch/finally构造中手动处理,则异常由CLR处理。
释放资源
必须释放I/O资源。可以使用Dispose方法在finally子句中手动释放资源。using关键字可用于自动释放资源。此外,File类中的方法为我们释放了资源。
示例文本文件
在示例中,我们使用这个简单的文本文件:
The Battle of Thermopylae was fought between an alliance of Greek city-states, led by King Leonidas of Sparta, and the Persian Empire of Xerxes I over the course of three days, during the second Persian invasion of Greece.
C#文件.ReadAllText
File提供用于创建、复制、删除、移动和打开单个文件的静态方法,并有助于创建FileStream对象。
File.ReadAllText打开一个文件,读取文件中指定编码的所有文本,然后关闭文件。
using System.Text; var path = "/home/janbodnar/Documents/thermopylae.txt"; var text = File.ReadAllText(path, Encoding.UTF8); Console.WriteLine(text);
程序读取thermopylae.txt文件的内容并将它们打印到控制台。
var text = File.ReadAllText(path, Encoding.UTF8);
我们一次将整个文件读入一个字符串。在第二个参数中,我们指定编码。
$ dotnet run The Battle of Thermopylae was fought between an alliance of Greek city-states, led by King Leonidas of Sparta, and the Persian Empire of Xerxes I over the course of three days, during the second Persian invasion of Greece.
C#文件.ReadAllLines
File.ReadAllLines打开一个文本文件,将文件的所有行读入字符串数组,然后关闭文件。
File.ReadAllLines是一种在C#中读取文件的便捷方法。处理非常大的文件时不应使用它。
var path = "/home/janbodnar/Documents/thermopylae.txt";
string[] lines = File.ReadAllLines(path);
foreach (string line in lines)
{
Console.WriteLine(line);
}
该示例将文件中的所有行读取到字符串数组中。我们在foreach循环中遍历数组并将每一行打印到控制台。
C#创建文件
File.CreateText创建或打开用于写入UTF-8编码文本的文件。如果该文件已经存在,其内容将被覆盖。
var path = "/home/janbodnar/Documents/cars.txt";
using var sw = File.CreateText(path);
sw.WriteLine("Hummer");
sw.WriteLine("Skoda");
sw.WriteLine("BMW");
sw.WriteLine("Volkswagen");
sw.WriteLine("Volvo");
在示例中,我们创建了一个cars.txt文件,并在其中写入了一些汽车名称。
using var sw = File.CreateText(path);
CreateText方法创建或打开用于写入UTF-8编码文本的文件。它返回一个StreamWriter对象。
sw.WriteLine("Hummer");
sw.WriteLine("Skoda");
...
我们向流中写入两行。
C#创建、上次写入、上次访问时间
通过File类,我们可以获得文件的创建、最后写入和最后访问时间。Exists方法确定指定文件是否存在。
var path = "cars.txt";
if (File.Exists(path))
{
Console.WriteLine(File.GetCreationTime(path));
Console.WriteLine(File.GetLastWriteTime(path));
Console.WriteLine(File.GetLastAccessTime(path));
}
如果指定的文件存在,我们确定它的创建、最后写入和最后访问时间。
if (File.Exists(path))
Exists方法返回true如果调用者具有所需的权限并且路径包含现有文件的名称;否则,false。如果路径为null、无效路径或零长度字符串,此方法也会返回false。
Console.WriteLine(File.GetCreationTime(path)); Console.WriteLine(File.GetLastWriteTime(path)); Console.WriteLine(File.GetLastAccessTime(path));
我们获取指定文件的创建时间、最后写入时间和最后访问时间。
$ dotnet run 2/25/2021 11:41:19 AM 2/25/2021 11:41:19 AM 2/25/2021 11:41:27 AM
C#复制文件
File.Copy方法将现有文件复制到新文件。它允许覆盖同名文件。
var srcPath = "/home/janbodnar/Documents/cars.txt";
var destPath = "/home/janbodnar/Documents/cars2.txt";
File.Copy(srcPath, destPath, true);
Console.WriteLine("File copied");
然后我们将文件的内容复制到另一个文件。
var srcPath = "/home/janbodnar/Documents/cars.txt"; var destPath = "/home/janbodnar/Documents/cars2.txt";
File.Copy(srcPath, destPath, true);
Copy方法复制文件。第三个参数指定是否应该覆盖文件,如果它存在。
C#IDisposable接口
Streams实现了IDisposable接口。实现此接口的对象必须尽早手动释放。这是通过在finally块中调用Dispose方法或使用using语句来完成的。
StreamReader? sr = null;
var path = "thermopylae.txt";
try
{
sr = new StreamReader(path);
Console.WriteLine(sr.ReadToEnd());
}
catch (IOException e)
{
Console.WriteLine("Cannot read file");
Console.WriteLine(e.Message);
}
catch (UnauthorizedAccessException e)
{
Console.WriteLine("Cannot access file");
Console.WriteLine(e.Message);
}
finally
{
sr?.Dispose();
}
在这个例子中,我们从磁盘上的文件中读取字符。我们手动释放分配的资源。
sr = new StreamReader(path); Console.WriteLine(sr.ReadToEnd());
StreamReader类用于读取字符。它的父级实现了IDisposable接口。
} catch (IOException e)
{
Console.WriteLine("Cannot read file");
Console.WriteLine(e.Message);
} catch (UnauthorizedAccessException e)
{
Console.WriteLine("Cannot access file");
Console.WriteLine(e.Message);
}
可能的异常在catch块中处理。
finally
{
sr?.Dispose();
}
在finally块中,Dispose方法清理资源。使用空条件运算符,我们仅在变量不为null时才调用该方法。p>
C#using语句
using语句定义了一个范围,对象将在该范围结束时被释放。它提供了一种方便的语法,可确保正确使用IDisposable对象。
var path = "/home/janbodnar/Documents/thermopylae.txt";
using (var sr = new StreamReader(path))
{
Console.WriteLine(sr.ReadToEnd());
}
该示例读取thermopylae.txt文件的内容。使用using语句释放资源。如果我们不处理IO异常,它们将由CLR处理。
C#使用声明
using声明是一个以using关键字开头的变量声明。它告诉编译器被声明的变量应该放在封闭范围的末尾。using声明从C#8.0开始可用。
var path = "thermopylae.txt"; using var sr = new StreamReader(path); Console.WriteLine(sr.ReadToEnd());
该示例读取thermopylae.txt文件的内容。当sr变量超出范围(在主要方法。)。
C#内存流
MemoryStream是一种处理计算机内存中数据的流。
using var ms = new MemoryStream(6);
ms.WriteByte(9);
ms.WriteByte(11);
ms.WriteByte(6);
ms.WriteByte(8);
ms.WriteByte(3);
ms.WriteByte(7);
ms.Position = 0;
int rs = ms.ReadByte();
do
{
Console.WriteLine(rs);
rs = ms.ReadByte();
} while (rs != -1);
我们使用MemoryStream将六个数字写入内存。然后我们读取这些数字并将它们打印到控制台。
using var ms = new MemoryStream(6);
该行创建并初始化了一个容量为6个字节的MemoryStream对象。
ms.WriteByte(9); ms.WriteByte(11); ms.WriteByte(6); ...
WriteByte方法在当前位置向当前流写入一个字节。
ms.Position = 0;
我们使用Position属性将光标在流中的位置设置为开头。
do
{
Console.WriteLine(rs);
rs = ms.ReadByte();
} while (rs != -1);
这里我们从流中读取所有字节并将它们打印到控制台。
$ dotnet run 9 11 6 8 3 7
C#流阅读器
StreamReader从字节流中读取字符。它默认为UTF-8编码。
var path = "/home/janbodnar/Documents/thermopylae.txt";
using var sr = new StreamReader(path);
while (sr.Peek() >= 0)
{
Console.WriteLine(sr.ReadLine());
}
我们读取文件的内容。这次我们使用ReadLine方法逐行读取文件。
while (sr.Peek() >= 0)
{
Console.WriteLine(sr.ReadLine());
}
Peek方法返回下一个可用字符但不使用它。它指示我们是否可以再次调用ReadLine方法。如果没有要读取的字符,它返回-1。
C#计算行数
在下一个示例中,我们计算行数。
int count = 0;
var path = "/home/janbodnar/Documents/thermopylae.txt";
using var sr = new StreamReader(path);
while (sr.ReadLine() != null)
{
count++;
}
Console.WriteLine($"There are {count} lines");
我们使用一个StreamReader和一个while循环。
while(stream.ReadLine() != null)
{
count++;
}
在while循环中,我们使用ReadLine方法从流中读取一行。它返回流中的一行,如果到达输入流的末尾,则返回null。
$ dotnet run There are 3 lines
C#流写器
StreamWriter以特定编码将字符写入流。
var path = "/home/janbodnar/Documents/newfile.txt";
using var sw = new StreamWriter(path);
sw.WriteLine("Today is a beautiful day.");
该示例使用StreamWriter将字符串写入文件。
using var sw = new StreamWriter(path);
我们创建一个新的StreamWriter。默认编码是UTF-8。StreamWriter将路径作为参数。如果文件存在,则覆盖;否则,将创建一个新文件。
C#文件流
FileStream提供文件流,支持同步和异步读写操作。
StreamReader和StreamWriter处理文本数据,而FileStream处理字节。
using System.Text; var path = "/home/janbodnar/Documents/newfile2.txt"; using var fs = new FileStream(path, FileMode.Append); var text = "ФÑÐ´Ð¾Ñ ÐÐ¸Ñ Ð°Ð¹Ð»Ð¾Ð²Ð¸Ñ ÐоÑÑоевÑкий\n"; byte[] bytes = new UTF8Encoding().GetBytes(text); fs.Write(bytes, 0, bytes.Length);
我们用俄语西里尔文将一些文本写入文件。
using System.Text;
UTF8Encoding类位于System.Text命名空间中。
using var fs = new FileStream(path, FileMode.Append);
FileStream对象被创建。第二个参数是打开文件的模式。追加模式打开文件(如果存在)并查找到文件末尾,或者创建一个新文件。
var text = "ФÑÐ´Ð¾Ñ ÐÐ¸Ñ Ð°Ð¹Ð»Ð¾Ð²Ð¸Ñ ÐоÑÑоевÑкий";
这是俄语西里尔文文本。
byte[] bytes = new UTF8Encoding().GetBytes(text);
根据俄语西里尔文文本创建一个字节数组。
fs.Write(bytes, 0, bytes.Length);
我们将字节写入文件流。
$ cat /home/janbodnar/Documents/newfile2.txt ФÑÐ´Ð¾Ñ ÐÐ¸Ñ Ð°Ð¹Ð»Ð¾Ð²Ð¸Ñ ÐоÑÑоевÑкий
我们显示创建文件的内容。
C#XmlTextReader
我们可以使用流来读取XML数据。XmlTextReader是在C#中读取XML文件的类。该类是只进和只读的。
我们有以下XML测试文件:
<?xml version="1.0" encoding="utf-8" ?>
<languages>
<language>Python</language>
<language>Ruby</language>
<language>Javascript</language>
<language>C#</language>
</languages>
此文件包含自定义XML标记之间的语言名称。
using System.Xml;
string path = "/home/janbodnar/Documents/languages.xml";
using var xreader = new XmlTextReader(path);
xreader.MoveToContent();
while (xreader.Read())
{
var node = xreader.NodeType switch
{
XmlNodeType.Element => String.Format("{0}: ", xreader.Name),
XmlNodeType.Text => String.Format("{0} \n", xreader.Value),
_ => ""
};
Console.Write(node);
}
此示例从XML文件中读取数据并将其打印到终端。
using System.Xml;
System.Xml命名空间包含Xml读写相关的类。
using var xreader = new XmlTextReader(path);
XmlTextReader对象被创建。它是一个阅读器,提供对XML数据的快速、非缓存、只进访问。它以文件名作为参数。
xreader.MoveToContent();
MoveToContent方法移动到XML文件的实际内容。
while (xreader.Read())
这一行从流中读取下一个节点。如果没有剩余节点,Read方法返回false。
var node = xreader.NodeType switch
{
XmlNodeType.Element => String.Format("{0}: ", xreader.Name),
XmlNodeType.Text => String.Format("{0} \n", xreader.Value),
_ => ""
};
Console.Write(node);
这里我们打印元素名称和元素文本。
$ dotnet run language: Python language: Ruby language: Javascript language: C#
C#创建、移动目录
System.IO.Directory是一个具有静态方法的类,用于创建、移动和枚举目录和子目录。
Directory.CreateDirectory("temp");
Directory.CreateDirectory("newdir");
Directory.Move("temp", "temporary");
我们创建两个目录并重命名其中一个。目录在项目文件夹中创建。
Directory.CreateDirectory("temp");
CreateDirectory方法创建一个新目录。
Directory.Move("temp", "temporary");
Move方法为指定目录赋予新名称。
C#目录信息
DirectoryInfo公开了用于创建、移动和枚举目录和子目录的实例方法。
var path = "/home/janbodnar/Documents";
var dirInfo = new DirectoryInfo(path);
string[] files = Directory.GetFiles(path);
DirectoryInfo[] dirs = dirInfo.GetDirectories();
foreach (DirectoryInfo subDir in dirs)
{
Console.WriteLine(subDir.Name);
}
foreach (string fileName in files)
{
Console.WriteLine(fileName);
}
我们使用DirectoryInfo类遍历特定目录并打印其内容。
var path = "/home/janbodnar/Documents"; var DirInfo = new DirectoryInfo(path);
我们显示指定目录的内容。
string[] files = Directory.GetFiles(path);
我们使用静态GetFiles方法获取目录的所有文件。
DirectoryInfo[] dirs = dir.GetDirectories();
我们得到所有的目录。
foreach (DirectoryInfo subDir in dirs)
{
Console.WriteLine(subDir.Name);
}
这里我们遍历目录并将它们的名称打印到控制台。
foreach (string fileName in files)
{
Console.WriteLine(fileName);
}
这里我们遍历文件数组并将它们的名称打印到控制台。
在本章中,我们介绍了C#中的输入/输出操作。
列出所有C#教程。
