服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - C#文件操作、读取文件、Debug/Trace类用法

C#文件操作、读取文件、Debug/Trace类用法

2023-02-24 15:12痴者工良 C#

这篇文章介绍了C#文件操作、读取文件、Debug/Trace类的用法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

1、文件操作

这段代码在 System.Private.CoreLib 下,对 System.IO.File 中的代码进行精简,供 CLR 使用。

当使用文件时,要提前判断文件路径是否存在,日常项目中要使用到文件的地方应该不少,可以统一一个判断文件是否存在的方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public static bool Exists(string? path)
{
    try
    {
        // 可以将 string? 改成 string
        if (path == null)
            return false;
        if (path.Length == 0)
            return false;
 
        path = Path.GetFullPath(path);
 
        // After normalizing, check whether path ends in directory separator.
        // Otherwise, FillAttributeInfo removes it and we may return a false positive.
        // GetFullPath should never return null
        Debug.Assert(path != null, "File.Exists: GetFullPath returned null");
        if (path.Length > 0 && PathInternal.IsDirectorySeparator(path[^1]))
        {
            return false;
        }
 
        return InternalExists(path);
    }
    catch (ArgumentException) { }
    catch (NotSupportedException) { } // Security can throw this on ":"
    catch (SecurityException) { }
    catch (IOException) { }
    catch (UnauthorizedAccessException) { }
 
    return false;
}

建议项目中对路径进行最终处理的时候,都转换为绝对路径:

?
1
Path.GetFullPath(path)

当然,相对路径会被 .NET 正确识别,但是对于运维排查问题和各方面考虑,绝对路径容易定位具体位置和排错。

在编写代码时,使用相对路径,不要写死,提高灵活性;在运行阶段将其转为绝对路径;

上面的 NotSupportedException 等异常是操作文件中可能出现的各种异常情况,对于跨平台应用来说,这些异常可能都是很常见的,提前将其异常类型识别处理,可以优化文件处理逻辑以及便于筛查处理错误。

2、读取文件

这段代码在 System.Private.CoreLib 中。

有个读取文件转换为 byte[] 的方法如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static byte[] ReadAllBytes(string path)
{
    // bufferSize == 1 used to avoid unnecessary buffer in FileStream
    using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 1))
    {
        long fileLength = fs.Length;
        if (fileLength > int.MaxValue)
            throw new IOException(SR.IO_FileTooLong2GB);
 
        int index = 0;
        int count = (int)fileLength;
        byte[] bytes = new byte[count];
        while (count > 0)
        {
            int n = fs.Read(bytes, index, count);
            if (n == 0)
                throw Error.GetEndOfFile();
            index += n;
            count -= n;
        }
        return bytes;
    }
}

可以看到 FileStream 的使用,如果单纯是读取文件内容,可以参考里面的代码:

?
1
2
3
4
5
FileStream fs = new FileStream(path,
                               FileMode.Open,
                               FileAccess.Read,
                               FileShare.Read,
                               bufferSize: 1)

上面的代码同样也存在 File.ReadAllBytes 与之对应, File.ReadAllBytes 内部是使用 InternalReadAllBytes 来处理文档读取:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private static byte[] InternalReadAllBytes(String path, bool checkHost)
{
    byte[] bytes;
    // 此 FileStream 的构造函数不是 public ,开发者不能使用
    using(FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read,
        FileStream.DefaultBufferSize, FileOptions.None, Path.GetFileName(path), false, false, checkHost)) {
        // Do a blocking read
        int index = 0;
        long fileLength = fs.Length;
        if (fileLength > Int32.MaxValue)
            throw new IOException(Environment.GetResourceString("IO.IO_FileTooLong2GB"));
        int count = (int) fileLength;
        bytes = new byte[count];
        while(count > 0) {
            int n = fs.Read(bytes, index, count);
            if (n == 0)
                __Error.EndOfFile();
            index += n;
            count -= n;
        }
    }
    return bytes;
}

这段说明我们可以放心使用 File 静态类中的函数,因为里面已经处理好一些逻辑了,并且自动释放文件。

如果我们手动 new FileStream ,则要判断一些情况,以免使用时报错,最好参考一下上面的代码。

.NET 文件流缓存大小默认是 4096 字节:

?
1
internal const int DefaultBufferSize = 4096;

这段代码在 File 类中定义,开发者不能设置缓存块的大小,大多数情况下,4k 是最优的块大小。

ReadAllBytes 的文件大小上限是 2 GB。

3、Debug 、Trace类

这两个类的命名空间为 System.Diagnostics,Debug 、Trace 提供一组有助于调试代码的方法和属性。

Debug 中的所有函数都不会在 Release 中有效,并且所有输出流不会在控制台显示,必须注册侦听器才能读取这些流。

Debug 可以打印调试信息并使用断言检查逻辑,使代码更可靠,而不会影响发运产品的性能和代码大小。

这类输出方法有 Write 、WriteLine 、 WriteIf 和 WriteLineIf 等,这里输出不会直接打印到控制台。

如需将调试信息打印到控制台,可以注册侦听器:

?
1
2
ConsoleTraceListener console = new ConsoleTraceListener();
Trace.Listeners.Add(console);

注意, .NET Core 2.x 以上 Debug 没有 Listeners ,因为 Debug 使用的是 Trace 的侦听器。

我们可以给 Trace.Listeners 注册侦听器,这样相对于 Debug 等效设置侦听器。

?
1
2
Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));
Debug.WriteLine("aa");

.NET Core 中的监听器都继承了 TraceListener,如 TextWriterTraceListener、ConsoleTraceListener、DefaultTraceListener。

如果需要输出到文件中,可以自行继承 TextWriterTraceListener ,编写文件流输出,也可以使用 DelimitedListTraceListener。

示例:

?
1
2
3
4
5
6
7
TraceListener listener = new DelimitedListTraceListener(@"C:\debugfile.txt");
 
        // Add listener.
        Debug.Listeners.Add(listener);
 
        // Write and flush.
        Debug.WriteLine("Welcome");

处理上述方法输出控制台,也可以使用

?
1
2
3
4
5
ConsoleTraceListener console=...
...Listeners.Add(console);
 
// 等效于
var console = new TextWriterTraceListener(Console.Out)

为了格式化输出流,可以使用 一下属性控制排版:

属性 说明
AutoFlush 获取或设置一个值,通过该值指示每次写入后是否应在 Flush() 上调用 Listeners。
IndentLevel 获取或设置缩进级别。
IndentSize 获取或设置缩进的空格数。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 1.
Debug.WriteLine("One");
 
// Indent and then unindent after writing.
Debug.Indent();
Debug.WriteLine("Two");
Debug.WriteLine("Three");
Debug.Unindent();
 
// End.
Debug.WriteLine("Four");
 
// Sleep.
System.Threading.Thread.Sleep(10000);
?
1
2
3
4
One
    Two
    Three
Four

.Assert() 方法对我们调试程序很有帮助,Assert 向开发人员发送一个强消息。在 IDE 中,断言会中断程序的正常操作,但不会终止应用程序。

.Assert() 的最直观效果是输出程序的断言位置。

?
1
2
3
4
5
6
7
8
9
Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));
int value = -1;
// A.
// If value is ever -1, then a dialog will be shown.
Debug.Assert(value != -1, "Value must never be -1.");
 
// B.
// If you want to only write a line, use WriteLineIf.
Debug.WriteLineIf(value == -1, "Value is -1.");
?
1
2
3
4
5
6
7
8
---- DEBUG ASSERTION FAILED ----
---- Assert Short Message ----
Value must never be -1.
---- Assert Long Message ----
 
   at Program.Main(String[] args) in ...Program.cs:line 12
 
Value is -1.

Debug.Prinf() 也可以输出信息,它跟 C 语言的 printf 函数行为一致,将后跟行结束符的消息写入,默认行终止符为回车符后跟一个换行符。

在 IDE 中运行程序时,使用 Debug.Assert()Trace.Assert() 等方法 ,条件为 false 时,IDE 会断言,这相当于条件断点。

在非 IDE 环境下,程序会输出一些信息,但不会有中断效果。

?
1
2
Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));
Trace.Assert(false);
?
1
2
Process terminated. Assertion Failed
   at Program.Main(String[] args) in C:\ConsoleApp4\Program.cs:line 44

个人认为,可以将 Debug、Trace 引入项目中,与日志组件配合使用。Debug、Trace 用于记录程序运行的诊断信息,便于日后排查程序问题;日志用于记录业务过程,数据信息等。

.Assert() 的原理, 在 true 时什么都不做;在 false 时调用 Fail 函数;如果你不注册侦听器的话,默认也没事可做。

.Assert() 唯一可做的事情是等条件为 false 时,执行 Fail 方法,当然我们也可以手动直接调用 Fail 方法,Fail 的代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static void Fail(string message) {
            if (UseGlobalLock) {
                lock (critSec) {
                    foreach (TraceListener listener in Listeners) {
                        listener.Fail(message);
                        if (AutoFlush) listener.Flush();
                    }           
                }
            }
            else {
                foreach (TraceListener listener in Listeners) {
                    if (!listener.IsThreadSafe) {
                        lock (listener) {
                            listener.Fail(message);
                            if (AutoFlush) listener.Flush();
                        }
                    }
                    else {
                        listener.Fail(message);
                        if (AutoFlush) listener.Flush();
                    }
                }           
            }
        }

到此这篇关于C#文件操作、读取文件、Debug/Trace类用法的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://www.cnblogs.com/whuanle/p/14141213.html

延伸 · 阅读

精彩推荐
  • C#C#自定义事件之属性改变引发事件示例

    C#自定义事件之属性改变引发事件示例

    这篇文章主要为大家详细介绍了C#自定义事件之属性改变引发事件示例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    cnc4532022-01-17
  • C#C#编写COM组件的方法分析

    C#编写COM组件的方法分析

    这篇文章主要介绍了C#编写COM组件的方法,结合实例形式分析了C#编写COM组件的具体步骤与相关实现技巧,需要的朋友可以参考下...

    廖先生6282022-01-10
  • C#C#使用dynamic类型访问JObject对象

    C#使用dynamic类型访问JObject对象

    这篇文章主要为大家详细介绍了C#使用dynamic类型访问JObject对象,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    ZKEASOFT10172022-02-22
  • C#c# Linq distinct不会调用Equals方法详解

    c# Linq distinct不会调用Equals方法详解

    这篇文章主要介绍了c# Linq distinct不会调用Equals方法详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    程序园7422022-10-20
  • C#Dynamic和Var的区别及dynamic使用详解

    Dynamic和Var的区别及dynamic使用详解

    C#中的很多关键词用法比较容易混淆,var和dynamic就是其中一组,他们都可以申明动态类型的变量,但是本质上他们还是有不少区别的,下面通过本文给大家...

    JackWang-CUMT4572021-11-08
  • C#c# 用ICSharpCode组件压缩文件

    c# 用ICSharpCode组件压缩文件

    今天,主要总结下,windows平台下,zip的压缩与解压的方法,用ICSharpCode组件,有此需求的朋友可以参考下本文...

    micDavid4152022-11-23
  • C#C# 基于消息发布订阅模型的示例(上)

    C# 基于消息发布订阅模型的示例(上)

    这篇文章主要介绍了C# 基于消息发布订阅模型的示例,帮助大家更好的理解和学习使用c#,感兴趣的朋友可以了解下...

    Hello——寻梦者!3962022-11-03
  • C#C#实现QQ窗口抖动效果

    C#实现QQ窗口抖动效果

    这篇文章主要为大家详细介绍了C#实现QQ窗口抖动效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    IT-Xu_LongTeng5442022-10-18