当前位置: 首页 > news >正文

【C#教程16/16】: 输入输出

一、提要

        本文专门介绍 C# 中的输入和输出。 C# 中的输入和输出基于流。文件流、文件建立修改等属性操作,文件目录操作等。

二、C# input和output

2.1 C# 流

        流是字节序列的抽象,例如文件、输入/输出设备、进程间通信管道或 TCP/IP 套接字。流将数据从一个点传输到另一个点。流也能够操纵数据;例如,他们可以压缩或加密数据。在 .NET Framework 中,System.IO 命名空间包含能够读取和写入数据流和文件的类型。

        C# 在 File 类中为 I/O 操作提供高级方法,在 StreamReader 或 StreamWriter 等类中提供低级方法。

2.2 处理异常(exceptions)

        I/O 操作容易出错。我们可能会遇到异常,例如 FileNotFoundException 或         UnauthorizedAccessException。与 Java 不同,C# 不强制程序员手动处理异常。是否手动处理异常由程序员决定。如果异常没有在 try/catch/finally 结构中手动处理,则由 CLR 处理异常。

2.3 发行资源(Releasing resources)

        必须释放 I/O 资源。可以使用 Dispose 方法在 finally 子句中手动释放资源。 using 关键字可用于自动释放资源。此外,File 类中的方法会为我们释放资源。

2.4 文本文件示例

        在示例中,我们使用这个简单的文本文件:

        thermopylae.txt

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.ssan

三、读取整个文件 (File.ReadAllText)

        File 为单个文件的创建、复制、删除、移动和打开提供静态方法,并有助于创建 FileStream 对象。

Note: The File.ReadAllText is not suited for reading very large files.

        File.ReadAllText 打开一个文件,读取文件中具有指定编码的所有文本,然后关闭文件。

Program.cs

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.

四、逐行读取文件( File.ReadAllLines)

4.1 逐行读取

        File.ReadAllLines 打开一个文本文件,将文件的所有行读入一个字符串数组,然后关闭文件。 File.ReadAllLines 是一种在 C# 中读取文件的便捷方法。处理非常大的文件时不应使用它。

Program.cs

var path = "/home/janbodnar/Documents/thermopylae.txt";

string[] lines = File.ReadAllLines(path);

foreach (string line in lines)
{
    Console.WriteLine(line);
}

        该示例将文件中的所有行读取到字符串数组中。我们在 foreach 循环中遍历数组并将每一行打印到控制台。

4.2 生成逐行文件( create file )

        File.CreateText 创建或打开用于写入 UTF-8 编码文本的文件。如果文件已经存在,则覆盖其内容。

Program.cs

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");
...

        我们向流中写入两行。

五、获取文件建立时间、最后写入、最后访问时间

        通过 File 类,我们可以获得文件的创建、最后写入和最后访问时间。 Exists 方法确定指定文件是否存在。

Program.cs

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;否则,错误。如果 path 为 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

六、拷贝文件

        File.Copy 方法将现有文件复制到新文件。它允许覆盖同名文件。

Program.cs

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 方法复制文件。第三个参数指定文件是否应该被覆盖,如果它存在的话。

七、 异常接口(IDisposable interface)

        流实现了 IDisposable 接口。实现此接口的对象必须尽早手动处置。这是通过在 finally 块中调用 Dispose 方法或使用 using 语句来完成的。

Program.cs

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 方法清理资源。使用空条件运算符,我们仅在变量不为空时才调用该方法。

八、用using 定义语句

        using 语句定义了一个范围,对象将在该范围结束。它提供了一种方便的语法,可确保正确使用 IDisposable 对象。

Program.cs

var path = "/home/janbodnar/Documents/thermopylae.txt";

using (var sr = new StreamReader(path))
{
    Console.WriteLine(sr.ReadToEnd());
}

        该示例读取thermopylae.txt 文件的内容。使用 using 语句释放资源。如果我们不处理 IO 异常,它们将由 CLR 处理。

九、using 声明

        using 声明是一个变量声明,前面有 using 关键字。它告诉编译器被声明的变量应该放在封闭范围的末尾。 using 声明自 C# 8.0 起可用。

Program.cs

var path = "thermopylae.txt";
using var sr = new StreamReader(path);
Console.WriteLine(sr.ReadToEnd());

        该示例读取thermopylae.txt 文件的内容。当 sr 变量超出范围时(在 Main 方法结束时),资源会自动清理。

十、内存流MemoryStream

        MemoryStream 是一种处理计算机内存中数据的流。

Program.cs

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
7s

十一、读取流( StreamReader)

        StreamReader 从字节流中读取字符。它默认为 UTF-8 编码。

Program.cs

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。

十二、行统计(count lines)

        在下一个示例中,我们将计算行数。

Program.cs

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");

We use a StreamReader and a while loop.

while(stream.ReadLine() != null)
{
    count++;
}

        在 while 循环中,我们使用 ReadLine 方法从流中读取一行。它从流中返回一行,如果到达输入流的末尾,则返回 null。

$ dotnet run
There are 3 linessshi

十三、流写入( StreamWriter)

        StreamWriter 以特定编码将字符写入流。

Program.cs

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 将路径作为参数。如果文件存在,则覆盖;否则,将创建一个新文件。

十四、文件流(FileStream)

        FileStream 为文件提供流,支持同步和异步读写操作。StreamReader 和 StreamWriter 处理文本数据,而 FileStream 处理字节。

Program.cs

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 .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
Фёдор Михайлович Достоевский

        我们显示创建的文件的内容。

十五、xml文件读取(XmlTextReader)

        我们可以使用流来读取 XML 数据。 XmlTextReader 是在 C# 中读取 XML 文件的类。该类是只进的和只读的。

        我们有以下 XML 测试文件:

languages.xml

<?xml version="1.0" encoding="utf-8" ?>
<languages>
    <language>Python</language>
    <language>Ruby</language>
    <language>Javascript</language>
    <language>C#</language>
</languages>

        此文件包含自定义 XML 标记之间的语言名称。

Program.cs

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#

十六、生成和转移create, move directory

        System.IO.Directory 是一个类,它具有用于创建、移动和枚举目录和子目录的静态方法。

Program.cs

Directory.CreateDirectory("temp");
Directory.CreateDirectory("newdir");
Directory.Move("temp", "temporary");

        我们创建两个目录并重命名创建的目录之一。目录是在项目文件夹中创建的。

Directory.CreateDirectory("temp");
CreateDirectory 方法创建一个新目录。
Directory.Move("temp", "temporary");

Move 方法为指定的目录提供了一个新名称。

十七、目录信息(DirectoryInfo)

        DirectoryInfo 公开了用于创建、移动和枚举目录和子目录的实例方法。

Program.cs

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# 中的输入/输出操作。

相关文章:

  • 银行 IT 架构到底要不要云化?
  • 柔性电子数字源表测试方案
  • 统计学考研笔记:回归方程计算题
  • 嵌入式基础_STM32F103C8T6从零开始建立一个项目(库函数)
  • Node.js | 基础完结、综合训练 —— 路由应用实战教程
  • 生命在于学习——代码审计基础
  • 【第十四篇】- Maven 自动化构建
  • c++中如何利用VA_LIST 和单体模式,构建自己的log小系统
  • 【Django】开发日报_4_Day:手机号码管理系统(1)
  • Oracle中含替换变量的查询(二)
  • 论文解读:Sadeepcry:使用自我注意和自动编码器网络的蛋白质结晶倾向预测的深度学习框架
  • Android Framework 框架层 | AMS 定义与知识点梳理
  • 工业场景全流程!机器学习开发并部署服务到云端
  • 真无线耳机哪个好?真无线耳机性价比排行榜
  • IDL学习:语法基础-程序控制
  • 【翻译】babel对TC39装饰器草案的实现
  • AHK 中 = 和 == 等比较运算符的用法
  • Bootstrap JS插件Alert源码分析
  • css布局,左右固定中间自适应实现
  • css的样式优先级
  • iOS 系统授权开发
  • JavaScript/HTML5图表开发工具JavaScript Charts v3.19.6发布【附下载】
  • JavaScript类型识别
  • JS变量作用域
  • Linux快速复制或删除大量小文件
  • Python_网络编程
  • VuePress 静态网站生成
  • 诡异!React stopPropagation失灵
  • 力扣(LeetCode)22
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 用quicker-worker.js轻松跑一个大数据遍历
  • 智能合约Solidity教程-事件和日志(一)
  • 国内唯一,阿里云入选全球区块链云服务报告,领先AWS、Google ...
  • ​低代码平台的核心价值与优势
  • ​如何在iOS手机上查看应用日志
  • #gStore-weekly | gStore最新版本1.0之三角形计数函数的使用
  • #pragma once与条件编译
  • #每天一道面试题# 什么是MySQL的回表查询
  • (1)(1.8) MSP(MultiWii 串行协议)(4.1 版)
  • (ctrl.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“
  • (C语言)字符分类函数
  • (六)激光线扫描-三维重建
  • (入门自用)--C++--抽象类--多态原理--虚表--1020
  • (数据结构)顺序表的定义
  • (一)基于IDEA的JAVA基础10
  • (转) Face-Resources
  • (转)AS3正则:元子符,元序列,标志,数量表达符
  • (转)大型网站的系统架构
  • **登录+JWT+异常处理+拦截器+ThreadLocal-开发思想与代码实现**
  • .net core 6 使用注解自动注入实例,无需构造注入 autowrite4net
  • .net core使用RPC方式进行高效的HTTP服务访问
  • .NET/C# 解压 Zip 文件时出现异常:System.IO.InvalidDataException: 找不到中央目录结尾记录。
  • .NETCORE 开发登录接口MFA谷歌多因子身份验证
  • @synthesize和@dynamic分别有什么作用?
  • [ 隧道技术 ] cpolar 工具详解之将内网端口映射到公网