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