From 241a7c43dadc104092222ed3a9f2a9d7a9ae0cc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E8=82=B2=E6=96=B0?= Date: Wed, 5 Aug 2026 18:32:17 +0800 Subject: [PATCH] =?UTF-8?q?docs(note):=20=E6=96=B0=E5=A2=9E=20excel/?= =?UTF-8?q?=E6=AD=A3=E5=88=99/Nginx=20=E7=AC=94=E8=AE=B0=E5=B9=B6=E6=95=B4?= =?UTF-8?q?=E7=90=86=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 AGENTS.md 仓库说明与 excelize 依赖 - 新增正则表达式、go操作excel、Nginx常见错误、Golang底层架构笔记 - 布尔变量命名迁移至资源/编程/规范,删除随手记旧笔记 --- AGENTS.md | 36 ++++++++ go/go.mod | 12 +++ go/go.sum | 18 ++++ note/资源/DevOps/Nginx/常见错误.md | 45 ++++++++++ note/资源/编程/Golang/底层架构.md | 73 ++++++++++++++++ .../编程/规范}/布尔变量命名.md | 0 note/随手记/go.md | 3 - note/随手记/go操作excel.md | 83 +++++++++++++++++++ note/随手记/正则表达式.md | 72 ++++++++++++++++ 9 files changed, 339 insertions(+), 3 deletions(-) create mode 100644 AGENTS.md create mode 100644 go/go.sum create mode 100644 note/资源/DevOps/Nginx/常见错误.md create mode 100644 note/资源/编程/Golang/底层架构.md rename note/{随手记 => 资源/编程/规范}/布尔变量命名.md (100%) delete mode 100644 note/随手记/go.md create mode 100644 note/随手记/go操作excel.md create mode 100644 note/随手记/正则表达式.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..e51a379 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,36 @@ +# AGENTS.md + +## 仓库结构 + +按语言分层的个人练习仓库,每种语言一个顶层目录,各自独立管理依赖。 + +| 目录 | 说明 | +|---|---| +| `go/` | Go module `lab`(Go 1.26.2),go.mod 在该目录内 | +| `note/` | 个人笔记(非代码),`note/项目/` 被 gitignore | +| `dart/` | 预留,尚未建立 | + +## Go 约定 + +- **Module 在 `go/` 目录下,不在仓库根**——所有 go 命令需在 `go/` 内执行 +- 每个叶子目录 = 一个独立小程序(`package main` + `main.go`) +- 多个小程序可并存,不同目录、各自独立 +- 主题目录(basics/concurrency/stdlib/patterns/algorithms/web/testing/project)可随时增删 +- 测试文件用 `*_test.go`,运行单个测试:`cd go && go test ./testing/ -run TestSliceAppend` + +## 开发命令 + +``` +# Go——所有命令在 go/ 目录下执行 +cd go +go run ./<主题>/<程序名>/ # 运行单个程序 +go test ./... # 运行全部测试 +go test ./testing/ -v # 运行 testing 包测试 +go mod tidy # 整理依赖 +``` + +## 注意事项 + +- 仓库无 CI、无 pre-commit hooks、无代码检查——每个程序自行保证可编译即可 +- 笔记和练习代码严格分离:程序相关说明写在程序目录的 README.md,系统性知识放 `note/` +- `.gitignore` 已覆盖 Go 二进制、Dart/Flutter 构建产物、IDE 配置、操作系统文件 diff --git a/go/go.mod b/go/go.mod index 861c0f9..da5e791 100644 --- a/go/go.mod +++ b/go/go.mod @@ -1,3 +1,15 @@ module lab go 1.26.2 + +require ( + github.com/richardlehane/mscfb v1.0.7 // indirect + github.com/richardlehane/msoleps v1.0.6 // indirect + github.com/tiendc/go-deepcopy v1.7.2 // indirect + github.com/xuri/efp v0.0.1 // indirect + github.com/xuri/excelize/v2 v2.11.0 // indirect + github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 // indirect + golang.org/x/crypto v0.53.0 // indirect + golang.org/x/net v0.56.0 // indirect + golang.org/x/text v0.38.0 // indirect +) diff --git a/go/go.sum b/go/go.sum new file mode 100644 index 0000000..44cf10f --- /dev/null +++ b/go/go.sum @@ -0,0 +1,18 @@ +github.com/richardlehane/mscfb v1.0.7 h1:oeoiM0WE79vHwE8RpIYYvIAc8ajTH2mb6UZm55/+EB0= +github.com/richardlehane/mscfb v1.0.7/go.mod h1:pe0+IUIc0AHh0+teNzBlJCtSyZdFOGgV4ZK9bsoV+Jo= +github.com/richardlehane/msoleps v1.0.6 h1:9BvkpjvD+iUBalUY4esMwv6uBkfOip/Lzvd93jvR9gg= +github.com/richardlehane/msoleps v1.0.6/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= +github.com/tiendc/go-deepcopy v1.7.2 h1:Ut2yYR7W9tWjTQitganoIue4UGxZwCcJy3orjrrIj44= +github.com/tiendc/go-deepcopy v1.7.2/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ= +github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8= +github.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= +github.com/xuri/excelize/v2 v2.11.0 h1:HxaEFl6sRN2+8J5a8HaKq+0M4FsjBGMnWWtjOCPSG88= +github.com/xuri/excelize/v2 v2.11.0/go.mod h1:jxFLbzaIwGQ5ufFNvYfUOHqXhfPaNmP14KWfmNz2Uak= +github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 h1:+C0TIdyyYmzadGaL/HBLbf3WdLgC29pgyhTjAT/0nuE= +github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= +golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= +golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= +golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= +golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= +golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= +golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= diff --git a/note/资源/DevOps/Nginx/常见错误.md b/note/资源/DevOps/Nginx/常见错误.md new file mode 100644 index 0000000..2b391cc --- /dev/null +++ b/note/资源/DevOps/Nginx/常见错误.md @@ -0,0 +1,45 @@ +## Nginx 反代未透传客户端 IP 导致服务端日志全量变为 127.0.0.1 + +### 现象 + +后端解析IP后全量变成 `127.0.0.1`。 + +### 原因 + +后端解析IP的顺序是 +``` +X-Forwarded-For → X-Real-IP → RemoteAddr +``` + +**Nginx**转发至后端服务的 location 块缺少`proxy_set_header`指令: + +```nginx +location /olive3 { + + proxy_pass http://127.0.0.1:55430; + + proxy_connect_timeout 300s; + + proxy_send_timeout 300s; + + proxy_read_timeout 300s; + +} +``` + +### 修复 + +在缺失的 location 块中补充 header 透传: + +```nginx +location /api { + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Real-IP $remote_addr; + proxy_pass http://127.0.0.1:55430; + # ... 其他配置不变 +} +``` + +- `$proxy_add_x_forwarded_for`:将客户端真实 IP 追加到已有 `X-Forwarded-For` 尾部,而非覆盖 +- `$remote_addr`:Nginx 直接看到的对端地址,作为 `X-Real-IP` 传递 + diff --git a/note/资源/编程/Golang/底层架构.md b/note/资源/编程/Golang/底层架构.md new file mode 100644 index 0000000..0e2ccf0 --- /dev/null +++ b/note/资源/编程/Golang/底层架构.md @@ -0,0 +1,73 @@ +Go 1.24 起,`map` 的底层实现从传统的 `hmap` + `bmap` 哈希表,彻底切换到了 Swiss Table(瑞士表) + +![Gemini_Generated_Image_jhqwi7jhqwi7jhqw](D:\zhangyuxin\code\lab\note\assets\golang-map[swiss table].png) + + + +### Group内存布局图 + +你可以直接复制以下文本块到你的 Markdown 笔记中(使用 text 包裹): + +codeText + + + +``` ++-------------------------------------------------------------------------------+ +| 单 Group 内存布局 | ++--------------------------+----------------------------+-----------------------+ +| 1. Metadata 控制字节区 | 2. 连续 Key 存储区 | 3. 连续 Value 存储区 | ++--------------------------+----------------------------+-----------------------+ +| [M0][M1][M2]...[M7] | [K0][K1][K2]...[K7] | [V0][V1][V2]...[V7] | ++--------------------------+----------------------------+-----------------------+ +| <- 固定 8 个字节 -> | <- 长度取决于 Key 类型 -> | <- 长度取决于 Val -> | +| <- 一次性载入寄存器对比 -> | | +``` + +**为什么这样设计?(设计优势):** + +1. +2. **避免内存对齐填充(Padding)**:如果采用 [Meta][Key][Value] 交叉排列,由于不同类型的 Key/Value 字节大小不同,为了对齐,Go 必须在它们之间插入大量空白填充字节,浪费空间。分块连续存储消除了组内的对齐损耗。 +3. **对 CPU 缓存友好**:8 字节的 Metadata 紧密相连,一次 Cache Line 填充就能把整组的控制信息全部读入,极大地提高了缓存命中率。 + +------ + + + +### 读取流程 + +以下为您整理的完整、正确的读取逻辑,您可以直接复制使用: + +#### 1.哈希计算与分段 (Hash Splitting) + +- 将 Key 传入哈希函数,计算出一个 **64 位的哈希值**。 +- 该哈希值被拆分为两部分:**H1(高 57 位)**:用于定位哈希表中的初始 Group 索引。**H2(低 7 位)**:作为特征值(指纹),存储在 Group 的 Metadata 中,用于快速匹配。 + +#### 2.定位初始 Group (Locate Group) + +- 计算初始 Group 索引:group_index = H1 & (Group数量 - 1)。 + + > 可以理解与$2^B$进行取模运算,但是**位与运算**效率更高且等价。 + +#### 3.组内快速匹配 (Group-level SWAR Match) + +- **一次性加载**:将目标 Group 的 8 字节 Metadata 一次性载入一个 64 位寄存器中。 +- **并行对比(SWAR 技术)**:利用位运算,将 7 位的 H2 同时与这 8 个 Metadata 字节进行并行对比,输出一个 8 位的位掩码(Bitmask)。位掩码中为 1 的位,代表该位置的 H2 与目标 H2 匹配成功。这一步极快,完全避免了循环遍历。 + +#### 4.冲突解决与探测循环 (Verification & Probing Loop) + +根据位掩码的匹配结果,执行以下逻辑: + +1. 依次根据匹配成功的索引,去 **Key 存储区** 找到对应的真实 Key。 +2. 进行完整的 Key 等值对比(如 if key == targetKey):**若匹配成功**:直接去 **Value 存储区** 对应偏移量取出 Value 并返回。**若匹配失败**:说明发生了 **H2 哈希冲突**(即不同的 Key 产生了相同的 H2),继续对比下一个匹配的槽位。 +3. 如果当前 Group 中所有匹配了 H2 的槽位都对比完了,仍未找到匹配的 Key,则**跳转至【情况 B】**。 + +此时需要检查当前 Group 的 Metadata 状态,决定是继续向后寻找还是停止: + +- **若当前 Group 中存在“空位”(Empty,0xFF)**: + + 说明探测链在此处断开,该 Key 在 Map 中**一定不存在**,直接返回 nil 或零值。 + +- **若当前 Group 中无空位,但存在“墓碑”(Tombstone,0x80)或已满**: + + 虽然当前 Group 没找到,但可能因为历史删除操作(留下了墓碑)或哈希冲突,导致数据被存到了后续的 Group。不能停止!使用**二次探测算法(Quadratic Probing)**计算下一个 Group 的索引,跳转回 **【3.组内快速匹配】** 继续循环寻找。 diff --git a/note/随手记/布尔变量命名.md b/note/资源/编程/规范/布尔变量命名.md similarity index 100% rename from note/随手记/布尔变量命名.md rename to note/资源/编程/规范/布尔变量命名.md diff --git a/note/随手记/go.md b/note/随手记/go.md deleted file mode 100644 index 6e9cacc..0000000 --- a/note/随手记/go.md +++ /dev/null @@ -1,3 +0,0 @@ -Go 1.24 起,`map` 的底层实现从传统的 `hmap` + `bmap` 哈希表,彻底切换到了 Swiss Table(瑞士表) - -![Gemini_Generated_Image_jhqwi7jhqwi7jhqw](D:\zhangyuxin\code\lab\note\assets\golang-map[swiss table].png) diff --git a/note/随手记/go操作excel.md b/note/随手记/go操作excel.md new file mode 100644 index 0000000..e8fd7df --- /dev/null +++ b/note/随手记/go操作excel.md @@ -0,0 +1,83 @@ +## excelize库 + +读写Excel的Go库 + +[github](https://github.com/qax-os/excelize) + +## 基本用法 + +### 创建工作簿 + +```go + f := excelize.NewFile() + defer func() { + if err := f.Close(); err != nil { + fmt.Println(err) + } + }() + // 创建一个新的Sheet,返回的index为该工作簿的索引 + index, err := f.NewSheet("SheetName") + if err != nil { + return fmt.Errorf("创建Sheet失败: %w", err) + } +``` + +### 打开文件 + +```go + f, err := excelize.OpenFile(filePath) + if err != nil { + return fmt.Errorf("打开 Excel失败: %w", err) + } + defer func() { + if err := f.Close(); err != nil { + fmt.Println(err) + } + }() +``` + +### 写入cell值 + +```go + f.SetCellValue("SheetName", "B2", 100) +``` + +### 设置工作簿默认活动工作表 + +```go +f.SetActiveSheet(index) +``` + +### 创建或更新excel表 + +```go + if err := f.SaveAs("Book1.xlsx"); err != nil { + return fmt.Errorf("保存失败: %w", err) + } +``` + +### 获取指定cell值 + +```go + cell, err := f.GetCellValue("Sheet1", "B2") + if err != nil { + return fmt.Errorf("读取cell失败: %w", err) + } + fmt.Println(cell) +``` + +### 获取所有工作簿的cell值(二维数组) + +```go + rows, err := f.GetRows("Sheet1") + if err != nil { + return fmt.Errorf("读取失败: %w", err) + } + for _, row := range rows { + for _, colCell := range row { + fmt.Print(colCell, "\t") + } + fmt.Println() + } +``` + diff --git a/note/随手记/正则表达式.md b/note/随手记/正则表达式.md new file mode 100644 index 0000000..e002956 --- /dev/null +++ b/note/随手记/正则表达式.md @@ -0,0 +1,72 @@ +# 正则表达式 + +正则表达式是一种用于匹配和操作文本的强大工具,用于描述要匹配的文本模式。 + +## 匹配字符 + +### 普通匹配字符 + +| 字符 | 描述 | +| :------- | :----------------------------------------------------------- | +| `[xyz]` | 匹配 `[xyz]` 中列出的所有字符。例如 `[xyz]`,能够匹配目标的所有 x、y、z 字符。 | +| `[^xyz]` | 匹配 `[^xyz]` 中列出的字符外的所有字符。例如 `[^xyz]`,能够匹配目标的除去 x、y、z 外的所有字符。 | +| `[A-Z]` | `[A-Z]` 表示一个区间,匹配所有大写字母;`[a-z]` 匹配所有小写字母;`[0-9]` 匹配所有数字。 | +| `[\s\S]` | 匹配所有字符(包括换行符)。`\s` 匹配所有空白符(包括换行),`\S` 匹配所有非空白符(不包括换行),两者合并即可匹配任意字符。 | +| `.` | 匹配除换行符 `\n`、`\r` 之外的任何单个字符,等价于 `[^\n\r]`。 | +| `\w` | 匹配字母、数字、下划线,等价于 `[A-Za-z0-9_]`。 | +| `\d` | 匹配任意一个阿拉伯数字(0 到 9),等价于 `[0-9]`。 | + +### 非打印字符 + +| 字符 | 描述 | +| :---- | :----------------------------------------------------------- | +| `\cx` | 匹配由 x 指明的控制字符。例如,`\cM` 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或 a-z 之一,否则将 c 视为一个原义的 'c' 字符。 | +| `\f` | 匹配一个换页符,等价于 `\x0c` 和 `\cL`。 | +| `\n` | 匹配一个换行符,等价于 `\x0a` 和 `\cJ`。 | +| `\r` | 匹配一个回车符,等价于 `\x0d` 和 `\cM`。 | +| `\s` | 匹配任何空白字符,包括空格、制表符、换页符等,等价于 `[ \f\n\r\t\v]`。注意 Unicode 正则表达式会匹配全角空格符。 | +| `\S` | 匹配任何非空白字符,等价于 `[^ \f\n\r\t\v]`。 | +| `\t` | 匹配一个制表符,等价于 `\x09` 和 `\cI`。 | +| `\v` | 匹配一个垂直制表符,等价于 `\x0b` 和 `\cK`。 | + +### 特殊字符 + +| 特殊字符 | 描述 | +| :------- | :----------------------------------------------------------- | +| `$` | 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 `$` 也匹配 `\n` 或 `\r`。要匹配 `$` 字符本身,请使用 `\$`。 | +| `( )` | 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 `\(` 和 `\)`。 | +| `*` | 匹配前面的子表达式零次或多次。要匹配 `*` 字符本身,请使用 `\*`。 | +| `+` | 匹配前面的子表达式一次或多次。要匹配 `+` 字符本身,请使用 `\+`。 | +| `.` | 匹配除换行符 `\n` 之外的任何单字符。要匹配 `.` 本身,请使用 `\.`。 | +| `[` | 标记一个中括号表达式的开始。要匹配 `[`,请使用 `\[`。 | +| `?` | 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 `?` 字符本身,请使用 `\?`。 | +| `\` | 将下一个字符标记为特殊字符、原义字符、向后引用或八进制转义符。例如,`\\` 匹配 `\`。 | +| `^` | 匹配输入字符串的开始位置。在方括号表达式中使用时,表示不接受方括号内的字符集合(取反)。要匹配 `^` 字符本身,请使用 `\^`。 | +| `{` | 标记限定符表达式的开始。要匹配 `{`,请使用 `\{`。 | +| `|` | 指明两项之间的一个选择。要匹配 `|`,请使用 `\|`。 | + +### 限定符 + +| 字符 | 描述 | +| :------ | :----------------------------------------------------------- | +| `*` | 匹配前面的子表达式零次或多次。 | +| `+` | 匹配前面的子表达式一次或多次。 | +| `?` | 匹配前面的子表达式零次或一次,等价于 `{0,1}`。例如,`do(es)?` 可以匹配 "do" 和 "does",但不能匹配 "dog"。 | +| `{n}` | n 是一个非负整数,匹配确定的 n 次。例如,`o{2}` 不能匹配 "Bob" 中的 o,但是能匹配 "food" 中的两个 o。 | +| `{n,}` | n 是一个非负整数,至少匹配 n 次。例如,`o{2,}` 不能匹配 "Bob" 中的 o,但能匹配 "foooood" 中的所有 o。`o{1,}` 等价于 `o+`,`o{0,}` 等价于 `o*`。 | +| `{n,m}` | m 和 n 均为非负整数,其中 n <= m。最少匹配 n 次且最多匹配 m 次。例如,`o{1,3}` 将匹配 "fooooood" 中的前三个 o。`o{0,1}` 等价于 `o?`。 | + +## 常用正则表达式 + +| 正则表达式 | 匹配内容 | +| -------------------------- | ---------- | +| `[\u4e00-\u9fa5]` | 中文字符 | +| `\d{3}-\d{8}|\d{4}-\{7,8}` | 国内手机号 | +| | | +| | | +| | | +| | | +| | | +| | | +| | | +