修复 Excel 无法表示 1900 年之前日期的问题
# SpanHelper 文档
## 概述
`SpanHelper` æ˜¯ä¸€ä¸ªé™æ€å¸®åŠ©ç±»ï¼Œæä¾›äº† `Span<T>` å’Œ `ReadOnlySpan<T>` 的常用扩展方法。它专注于高性能ã€é›¶åˆ†é…çš„æ“作,包括å—符串编ç 转æ¢ã€åå…进制编ç ã€è¾¹ç•Œæœç´¢ã€æµæ“作和数æ®ä¿®å‰ªç‰åŠŸèƒ½ã€‚
## æ ¸å¿ƒç‰¹æ€§
- **零分é…转æ¢**:æä¾›å—节数组到å—符串的高效转æ¢
- **åå…进制编ç **:支æŒå¤šç§æ ¼å¼çš„åå…进制å—符串生æˆ
- **边界æœç´¢**:在å—节æµä¸æŸ¥æ‰¾ç‰¹å®šçš„开始和结æŸè¾¹ç•Œ
- **æµæ“作扩展**:扩展 `Stream` ä»¥æ”¯æŒ `Memory<byte>` 写入
- **æ•°æ®ä¿®å‰ª**:去除å‰åŽæŒ‡å®šçš„å…ƒç´
- **跨平å°å…¼å®¹**:兼容 .NET Framework 4.5 到 .NET 9
## å—符串编ç 转æ¢
### 基础转æ¢
```csharp
using NewLife;
// ReadOnlySpan<byte> 到å—符串
ReadOnlySpan<byte> data = stackalloc byte[] { 72, 101, 108, 108, 111 }; // "Hello"
string text = data.ToStr(); // 使用 UTF8 ç¼–ç
string gbkText = data.ToStr(Encoding.GetEncoding("GBK"));
// Span<byte> 到å—符串
Span<byte> buffer = stackalloc byte[] { 87, 111, 114, 108, 100 }; // "World"
string text2 = buffer.ToStr();
```
### é«˜æ€§èƒ½ç¼–ç æ“作
```csharp
// å—符串编ç 到 Span<byte>(é¿å…ä¸é—´æ•°ç»„分é…)
string text = "Hello World";
Span<byte> buffer = stackalloc byte[100];
int bytesWritten = Encoding.UTF8.GetBytes(text.AsSpan(), buffer);
// å—节解ç 为å—符串(指针路径,.NET Framework 4.5+ 兼容)
ReadOnlySpan<byte> utf8Bytes = stackalloc byte[] { 72, 101, 108, 108, 111 };
string decoded = Encoding.UTF8.GetString(utf8Bytes);
```
## åå…进制编ç
### 基础åå…进制转æ¢
```csharp
// 基础åå…进制编ç ï¼ˆå¤§å†™ï¼Œæ— åˆ†éš”ç¬¦ï¼‰
ReadOnlySpan<byte> data = stackalloc byte[] { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF };
string hex = data.ToHex(); // "0123456789ABCDEF"
// Span<byte> 版本
Span<byte> buffer = stackalloc byte[] { 0xFF, 0x00, 0x80 };
string hex2 = buffer.ToHex(); // "FF0080"
```
### é™åˆ¶é•¿åº¦çš„åå…进制
```csharp
byte[] largeData = new byte[1000];
// ... å¡«å……æ•°æ® ...
// åªæ˜¾ç¤ºå‰16å—节的åå…进制
string hex = largeData.AsSpan().ToHex(16); // åªè½¬æ¢å‰16å—节
// 如果数æ®å°‘于指定长度,显示全部
byte[] smallData = { 0x01, 0x02 };
string hex2 = smallData.AsSpan().ToHex(10); // "0102"
```
### 带分隔符和分组的åå…进制
```csharp
ReadOnlySpan<byte> data = stackalloc byte[] { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB };
// æ¯ä¸ªå—èŠ‚ç”¨ç©ºæ ¼åˆ†éš”
string hex1 = data.ToHex(" "); // "01 23 45 67 89 AB"
// æ¯2å—节用连å—符分隔
string hex2 = data.ToHex("-", groupSize: 2); // "0123-4567-89AB"
// æ¯4å—节用下划线分隔
string hex3 = data.ToHex("_", groupSize: 4); // "01234567_89AB"
// é™åˆ¶æ˜¾ç¤ºé•¿åº¦å¹¶åˆ†ç»„
string hex4 = data.ToHex(" ", groupSize: 0, maxLength: 4); // "01 23 45 67"
```
## 边界æœç´¢
### 基础边界æœç´¢
```csharp
// 查找边界之间的内容
ReadOnlySpan<byte> html = "<div>Hello World</div>"u8;
ReadOnlySpan<byte> startTag = "<div>"u8;
ReadOnlySpan<byte> endTag = "</div>"u8;
// æå–边界之间的内容
ReadOnlySpan<byte> content = html.Substring(startTag, endTag); // "Hello World"
// Span<byte> 版本
Span<byte> buffer = stackalloc byte[100];
// ... 填充 HTML 内容 ...
Span<byte> extracted = buffer.Substring(startTag, endTag);
```
### 获å–边界ä½ç½®ä¿¡æ¯
```csharp
ReadOnlySpan<byte> data = "prefix[content]suffix"u8;
ReadOnlySpan<byte> start = "["u8;
ReadOnlySpan<byte> end = "]"u8;
// 获å–详细ä½ç½®ä¿¡æ¯
var (offset, count) = data.IndexOf(start, end);
if (offset >= 0 && count >= 0)
{
// offset: 内容开始ä½ç½®
// count: 内容长度
var content = data.Slice(offset, count); // "content"
}
else if (offset >= 0 && count == -1)
{
// 找到开始边界但未找到结æŸè¾¹ç•Œ
Console.WriteLine("æœªæ‰¾åˆ°ç»“æŸæ ‡è®°");
}
else
{
// 未找到开始边界
Console.WriteLine("æœªæ‰¾åˆ°å¼€å§‹æ ‡è®°");
}
```
## æµæ“作扩展
### Memory 写入æµ
```csharp
// åŒæ¥å†™å…¥ ReadOnlyMemory<byte> 到æµ
ReadOnlyMemory<byte> data = new byte[] { 1, 2, 3, 4, 5 };
using var stream = new MemoryStream();
stream.Write(data); // æ‰©å±•æ–¹æ³•ï¼Œè‡ªåŠ¨å¤„ç†æ•°ç»„æ± å›žé€€
// 异æ¥å†™å…¥
await stream.WriteAsync(data);
await stream.WriteAsync(data, CancellationToken.None);
```
## æ•°æ®ä¿®å‰ª
### 基础修剪æ“作
```csharp
// 去除å‰åŽçš„é›¶å—节
ReadOnlySpan<byte> data = stackalloc byte[] { 0, 0, 1, 2, 3, 0, 0 };
ReadOnlySpan<byte> trimmed = data.Trim((byte)0); // [1, 2, 3]
// 去除å‰åŽçš„ç©ºæ ¼å—符
ReadOnlySpan<char> text = " Hello World ".AsSpan();
ReadOnlySpan<char> trimmedText = text.Trim(' '); // "Hello World"
// Span<T> 版本
Span<byte> buffer = stackalloc byte[] { 0xFF, 1, 2, 3, 0xFF, 0xFF };
Span<byte> trimmedBuffer = buffer.Trim((byte)0xFF); // [1, 2, 3]
```
## 最佳实践
1. **优先使用 ReadOnlySpan**:除éžéœ€è¦ä¿®æ”¹æ•°æ®ï¼Œå¦åˆ™ä½¿ç”¨åªè¯»ç‰ˆæœ¬
2. **æ ˆåˆ†é…å°ç¼“冲区**:256å—节以下优先使用 `stackalloc`
3. **å¤ç”¨ç¼–ç 器实例**:é¿å…é‡å¤åˆ›å»º `Encoding` 对象
4. **åˆç†ä½¿ç”¨ä¿®å‰ª**:é¿å…在大数æ®ä¸Šé¢‘ç¹ä¿®å‰ªæ“作
5. **错误处ç†**:边界æœç´¢å¯èƒ½è¿”å›žç©ºç»“æžœï¼Œéœ€è¦æ£€æŸ¥
6. **性能测试**:在性能关键路径进行基准测试
## 相关类型
- [`SpanReader`](/NewLife/X/Blob/dev/Doc/./SpanReader.md) - 高性能å—节æµè¯»å–器
- [`SpanWriter`](/NewLife/X/Blob/dev/Doc/./SpanWriter.md) - 高性能å—节æµå†™å…¥å™¨
- [`PooledByteBufferWriter`](/NewLife/X/Blob/dev/Doc/./PooledByteBufferWriter.md) - æ± åŒ–çš„åŠ¨æ€ç¼“冲区写入器
|