Merge branch 'main' of https://git.labmem.icu/labmem/lab
This commit is contained in:
@@ -36,5 +36,4 @@ dist/
|
|||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
||||||
# ---- Note 目录 ----
|
# ---- Note 目录 ----
|
||||||
note/assets/
|
|
||||||
note/项目/
|
note/项目/
|
||||||
@@ -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 配置、操作系统文件
|
||||||
@@ -1,3 +1,15 @@
|
|||||||
module lab
|
module lab
|
||||||
|
|
||||||
go 1.26.2
|
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
|
||||||
|
)
|
||||||
|
|||||||
@@ -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=
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package error
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MatchError struct {
|
||||||
|
code int
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *MatchError) Error() string {
|
||||||
|
return fmt.Sprintf("code: %d, error: %v", e.code, e.err)
|
||||||
|
}
|
||||||
|
func GetMatchError() error {
|
||||||
|
return &MatchError{code: 1, err: errors.New("123")}
|
||||||
|
}
|
||||||
|
func GetError() error {
|
||||||
|
return errors.New("123")
|
||||||
|
}
|
||||||
|
func TestError(t *testing.T) {
|
||||||
|
if err, ok := errors.AsType[*MatchError](GetMatchError()); ok {
|
||||||
|
t.Log(err)
|
||||||
|
} else {
|
||||||
|
t.Errorf("expected MatchError, got %T", GetMatchError())
|
||||||
|
}
|
||||||
|
if err, ok := errors.AsType[*MatchError](GetError()); ok {
|
||||||
|
t.Log(err)
|
||||||
|
} else {
|
||||||
|
t.Errorf("expected MatchError, got %T", GetError())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package net
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNet(t *testing.T) {
|
||||||
|
s := net.JoinHostPort("127.0.0.1", "8080")
|
||||||
|
|
||||||
|
t.Log(s)
|
||||||
|
t.Log(s)
|
||||||
|
addrs, err := net.LookupAddr("198.18.0.18")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(addrs)
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package regularmatch
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestChineseRegExp(t *testing.T) {
|
||||||
|
reg, err := regexp.Compile("[\u4e00-\u9fa5]")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to compile regex: %v", err)
|
||||||
|
}
|
||||||
|
matchStrSlice := reg.FindAllString("hello 世界", -1)
|
||||||
|
|
||||||
|
if len(matchStrSlice) != 2 {
|
||||||
|
t.Errorf("Expected 2 matches, got %d", len(matchStrSlice))
|
||||||
|
}
|
||||||
|
if matchStrSlice[0] != "世" || matchStrSlice[1] != "界" {
|
||||||
|
t.Errorf("Expected [世, 界], got %v", matchStrSlice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChinaMobile(t *testing.T) {
|
||||||
|
reg, err := regexp.Compile(`^1(3\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\d|9[0-35-9])\d{8}$`)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Failed to compile regex: %v", err)
|
||||||
|
}
|
||||||
|
// matchStrSlice := reg.FindAllString("123-12345678 1234-12345678", -1)
|
||||||
|
// if len(matchStrSlice) != 2 {
|
||||||
|
// t.Errorf("Expected 2 matches, got %d", len(matchStrSlice))
|
||||||
|
// }
|
||||||
|
matchStrSlice := reg.FindAllString("17623862704", -1)
|
||||||
|
t.Log(matchStrSlice[0])
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
@@ -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` 传递
|
||||||
|
|
||||||
@@ -1,3 +1,48 @@
|
|||||||
|
## 依赖与模块管理
|
||||||
|
|
||||||
|
| 命令 | 作用 | 示例 |
|
||||||
|
| ----------------- | ---------------------------------------- | ---------------------------------------- |
|
||||||
|
| `go mod init` | 初始化一个新模块(生成 go.mod) | `go mod init myapp` |
|
||||||
|
| `go mod tidy` | 自动添加缺失的依赖、删除未使用的依赖 | `go mod tidy` |
|
||||||
|
| `go get` | 为当前模块添加/升级/降级依赖 | `go get github.com/gin-gonic/gin@v1.9.0` |
|
||||||
|
| `go mod download` | 预下载 go.mod 中声明的所有依赖到本地缓存 | `go mod download` |
|
||||||
|
| `go mod vendor` | 将依赖复制到 vendor 目录(离线构建用) | `go mod vendor` |
|
||||||
|
| `go list` | 列出包或模块信息 | `go list -m all`(查看所有依赖版本) |
|
||||||
|
|
||||||
|
## 运行与安装
|
||||||
|
|
||||||
|
| 命令 | 作用 | 示例 |
|
||||||
|
| :----------- | :---------------------------------- | :----------------------------------- |
|
||||||
|
| `go run` | 编译并直接运行程序(适合调试) | `go run .` |
|
||||||
|
| `go install` | 编译并安装到 GOBIN 目录(全局可用) | `go install example.com/tool@latest` |
|
||||||
|
|
||||||
|
## 代码质量与格式化
|
||||||
|
|
||||||
|
| 命令 | 作用 | 示例 |
|
||||||
|
| :------- | :--------------------------- | :------------- |
|
||||||
|
| `go fmt` | 自动格式化代码,符合官方风格 | `go fmt ./...` |
|
||||||
|
| `go vet` | 静态检查代码中潜在的错误 | `go vet ./...` |
|
||||||
|
| `go fix` | 自动把旧版 API 更新为新 API | `go fix ./...` |
|
||||||
|
|
||||||
|
## 查看信息
|
||||||
|
|
||||||
|
| 命令 | 作用 | 示例 |
|
||||||
|
| :----------- | :--------------------- | :------------------------------------- |
|
||||||
|
| `go doc` | 查看包或函数的文档 | `go doc fmt.Println` |
|
||||||
|
| `go env` | 查看/修改 Go 环境配置 | `go env -w GOPROXY=https://goproxy.cn` |
|
||||||
|
| `go version` | 查看当前 Go 版本 | `go version` |
|
||||||
|
| `go help` | 查看某个命令的详细帮助 | `go help build` |
|
||||||
|
|
||||||
|
## 其他进阶命令
|
||||||
|
|
||||||
|
| 命令 | 作用 | 说明 |
|
||||||
|
| :------------ | :---------------------------------------------- | :------------------------------------------------------- |
|
||||||
|
| `go clean` | 清除缓存和编译产物 | `go clean -cache`、`go clean -modcache` |
|
||||||
|
| `go generate` | 运行源代码中的 `//go:generate` 指令自动生成代码 | 常用于生成代码模板 |
|
||||||
|
| `go tool` | 调用 Go 自带工具 | `go tool pprof`(性能分析)、`go tool trace`(协程跟踪) |
|
||||||
|
| `go work` | 多模块工作区管理 | `go work init`、`go work use ./xxx` |
|
||||||
|
| `go bug` | 打开浏览器提交 bug 报告 | 会自动附带系统信息 |
|
||||||
|
|
||||||
## 交叉编译
|
## 交叉编译
|
||||||
|
|
||||||
### 基本用法
|
### 基本用法
|
||||||
|
|||||||
@@ -115,3 +115,86 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 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()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
> Go 1.24 起,`map` 的底层实现从传统的 `hmap` + `bmap` 哈希表,彻底切换到了 Swiss Table(瑞士表)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 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.组内快速匹配】** 继续循环寻找。
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
## 命名规则
|
||||||
|
|
||||||
|
> [《布尔变量起名的艺术》](https://thatamazingprogrammer.com/posts/stop-naming-your-variables-flag-the-art-of-boolean-prefixes/)
|
||||||
|
|
||||||
|
### 使用正确的前缀
|
||||||
|
|
||||||
|
(1)**is**:描述事物的状态,后面跟形容词,比如 isActive,isDeleted,isEmpty。
|
||||||
|
|
||||||
|
(2)**has**:描述事物的所有权或包含关系,后面跟名词,比如 hasAccess,hasChildren,hasValidationErrors。
|
||||||
|
|
||||||
|
(3)**can**:描述事物的能力或权限,比如 canEdit,canDelete,canRetry。
|
||||||
|
|
||||||
|
(4)**should**:描述事物的意图或逻辑,比如 shouldRetry,shouldCacheResponse。
|
||||||
|
|
||||||
|
### 其他
|
||||||
|
|
||||||
|
永远不在布尔变量名中使用否定词,比如isDisabled,因为当使用在if语句中会让大脑进行双重否定运算
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
## `errors.AsType`
|
||||||
|
|
||||||
|
> *用泛型把* `errors.As` *的“先声明变量再传指针”简化为“直接返回* `(E, bool)`*”,是* `errors.As` *的语法糖升级*
|
||||||
|
|
||||||
|
**使用场景:**
|
||||||
|
|
||||||
|
- 与 `errors.As` 完全相同(按类型提取错误),但写起来更简洁
|
||||||
|
- 由于类型参数 `E` 必须是 `error` 接口的实现类型,**能在编译期避免 `errors.As` 那种“传错指针类型导致运行时 panic”的问题**
|
||||||
|
|
||||||
|
```go
|
||||||
|
// 传统 errors.As:需要先声明变量
|
||||||
|
var mysqlErr *mysql.MySQLError
|
||||||
|
if errors.As(err, &mysqlErr) {
|
||||||
|
return true, mysqlErr.Number
|
||||||
|
}
|
||||||
|
|
||||||
|
// errors.AsType(Go 1.26+):无需预先声明变量
|
||||||
|
if mysqlErr, ok := errors.AsType[*mysql.MySQLError](err); ok {
|
||||||
|
return true, mysqlErr.Number
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
-103
@@ -1,103 +0,0 @@
|
|||||||
## 内存逃逸
|
|
||||||
|
|
||||||
### 什么是逃逸
|
|
||||||
|
|
||||||
在函数内部创建的对象或者变量,当函数的生命周期结束后仍然被引用或持有,则该对象或者变量被称之为逃逸对象
|
|
||||||
|
|
||||||
### 影响
|
|
||||||
|
|
||||||
- 对象分配到堆上,而不是栈上。
|
|
||||||
- 堆内存由 Runtime 分配,由 GC 管理生命周期。
|
|
||||||
- 相比栈分配,堆分配开销更大(需要 Runtime 参与)。
|
|
||||||
- 增加 GC 扫描和回收压力,频繁逃逸可能影响程序性能。
|
|
||||||
- 对象生命周期变长,不再随着函数返回立即释放,而是等待 GC 回收。
|
|
||||||
- 如果多个 goroutine 共享该对象,可能产生数据竞争(Data Race),但这是共享对象导致的,不是逃逸本身导致的。
|
|
||||||
- 逃逸本身不会导致内存泄漏,但如果堆对象长期被引用,GC 无法回收,就会造成 Go 意义上的内存泄漏。
|
|
||||||
|
|
||||||
### 常见原因
|
|
||||||
|
|
||||||
1. 返回局部变量的指针
|
|
||||||
|
|
||||||
局部变量在函数返回后仍然需要使用,因此必须分配到堆上。
|
|
||||||
|
|
||||||
```go
|
|
||||||
func NewUser() *User {
|
|
||||||
u := User{}
|
|
||||||
return &u
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
2. 局部变量被闭包引用
|
|
||||||
|
|
||||||
闭包可能在函数返回后仍然执行,因此引用的局部变量需要继续存活。
|
|
||||||
|
|
||||||
```go
|
|
||||||
func f() func() int {
|
|
||||||
a := 1
|
|
||||||
return func() int {
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
3. 被 goroutine 引用
|
|
||||||
|
|
||||||
goroutine 的执行时间可能超过当前函数的生命周期。
|
|
||||||
|
|
||||||
```go
|
|
||||||
func f() {
|
|
||||||
a := 1
|
|
||||||
go func() {
|
|
||||||
fmt.Println(a)
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
4. 作为 interface 参数或返回值(部分情况)
|
|
||||||
|
|
||||||
如果编译器无法证明对象不会逃逸,就会保守地分配到堆上。
|
|
||||||
|
|
||||||
```go
|
|
||||||
func f() interface{} {
|
|
||||||
a := 10
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
> 注意:并不是所有 interface 都会逃逸。
|
|
||||||
|
|
||||||
5. 存入生命周期更长的对象
|
|
||||||
|
|
||||||
例如存入全局变量、堆对象或其他长期存在的数据结构。
|
|
||||||
|
|
||||||
```go
|
|
||||||
var users []*User
|
|
||||||
|
|
||||||
func f() {
|
|
||||||
u := User{}
|
|
||||||
users = append(users, &u)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
这里 u 必须逃逸,因为 users 在函数返回后仍然存在。
|
|
||||||
|
|
||||||
6. 编译器无法证明不会逃逸
|
|
||||||
|
|
||||||
```go
|
|
||||||
func g(x *User)
|
|
||||||
|
|
||||||
func f() {
|
|
||||||
u := User{}
|
|
||||||
g(&u)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
如果编译器无法确定 g 是否保存了 u 的地址,就会保守地让 u 逃逸到堆上。
|
|
||||||
|
|
||||||
### 逃逸分析命令
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go build -gcflags -m main.go
|
|
||||||
```
|
|
||||||
|
|
||||||
出现`moved to heap`或者`escapes to heap`说明存在逃逸
|
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# MySQL 字符集的终极巨坑 —— `utf8` vs `utf8mb4`
|
||||||
|
|
||||||
|
### 一句话总结
|
||||||
|
|
||||||
|
MySQL 中的 `utf8` 是个“假货”(最多只支持 3 字节),`utf8mb4` 才是真正的 UTF-8(支持 4 字节)。
|
||||||
|
|
||||||
|
### 为什么会有这个坑
|
||||||
|
|
||||||
|
- **历史遗留**:早期 MySQL 开发时,Unicode 字符确实只用 3 个字节就能存下。
|
||||||
|
- **现象**:一旦用户输入 **Emoji 表情**(如 😀)或部分**生僻字**(如 𰻝),数据库会直接抛出错误:`Incorrect string value`。
|
||||||
|
|
||||||
|
### 📊 核心对比
|
||||||
|
|
||||||
|
| 字符集 | 真实身份 | 最大字节数 | 能否存 Emoji | 状态 |
|
||||||
|
| :------------ | :----------------- | :--------- | :----------- | :--------- |
|
||||||
|
| **`utf8`** | `utf8mb3` (阉割版) | 3 字节 | ❌ 报错 | ⚠️ 已废弃 |
|
||||||
|
| **`utf8mb4`** | 真正的 UTF-8 | 4 字节 | ✅ 随便存 | ✅ 推荐使用 |
|
||||||
|
|
||||||
|
### 🛠️ 各版本现状与未来
|
||||||
|
|
||||||
|
- **MySQL 5.7 及以前**:默认字符集是 `latin1`,`utf8` 是重灾区。
|
||||||
|
- **MySQL 8.0 及以后**:**默认字符集已改为 `utf8mb4`**(新项目安全系数提升)。
|
||||||
|
- **官方态度**:目前 `utf8`(即 `utf8mb3`)已被标记为 Deprecated(废弃)。官方计划在**未来的某个大版本中彻底删除 `utf8mb3`**,并让 `utf8` 这个名字正式指向 `utf8mb4`。
|
||||||
@@ -0,0 +1,476 @@
|
|||||||
|
> 感觉还是不如typora好使啊,以后买个正版吧
|
||||||
|
|
||||||
|
```css
|
||||||
|
/**
|
||||||
|
* MarkText One Light Dark 主题 (仿 Typora onelight-dark:https://github.com/caolib/typora-onelight-theme)
|
||||||
|
*
|
||||||
|
* 使用方法:
|
||||||
|
* 在 MarkText 中打开 设置 → 主题 → 自定义 CSS,
|
||||||
|
* 将本文件全部内容粘贴进去,保存即可。
|
||||||
|
*
|
||||||
|
* 配色来源: Typora onelight-dark theme by caolib
|
||||||
|
* 兼容: MarkText v0.19.x (旧引擎) / v0.20+ (新引擎)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* ====================================================================
|
||||||
|
* CSS 变量 — 使用 camelCase 命名 (旧引擎 v0.19.x 兼容)
|
||||||
|
* ==================================================================== */
|
||||||
|
:root {
|
||||||
|
/* —— 编辑器核心 —— */
|
||||||
|
--themeColor: #5c99f3;
|
||||||
|
--themeColor90: rgba(92, 153, 243, 0.9);
|
||||||
|
--themeColor80: rgba(92, 153, 243, 0.8);
|
||||||
|
--themeColor70: rgba(92, 153, 243, 0.7);
|
||||||
|
--themeColor60: rgba(92, 153, 243, 0.6);
|
||||||
|
--themeColor50: rgba(92, 153, 243, 0.5);
|
||||||
|
--themeColor40: rgba(92, 153, 243, 0.4);
|
||||||
|
--themeColor30: rgba(92, 153, 243, 0.3);
|
||||||
|
--themeColor20: rgba(92, 153, 243, 0.2);
|
||||||
|
--themeColor10: rgba(92, 153, 243, 0.1);
|
||||||
|
--highlightThemeColor: #e94ab9;
|
||||||
|
|
||||||
|
--highlightColor: #ffffff10;
|
||||||
|
--selectionColor: #3266d0;
|
||||||
|
--editorColor: #c3c3c3;
|
||||||
|
--editorColor80: #c3c3c3;
|
||||||
|
--editorColor60: rgba(195, 195, 195, 0.6);
|
||||||
|
--editorColor50: rgba(195, 195, 195, 0.5);
|
||||||
|
--editorColor40: rgba(195, 195, 195, 0.4);
|
||||||
|
--editorColor30: rgba(195, 195, 195, 0.3);
|
||||||
|
--editorColor10: rgba(195, 195, 195, 0.1);
|
||||||
|
--editorColor04: rgba(195, 195, 195, 0.04);
|
||||||
|
--editorBgColor: #1f1f1e;
|
||||||
|
--editorAreaWidth: 900px;
|
||||||
|
--deleteColor: #ff6b6b;
|
||||||
|
--iconColor: rgba(195, 195, 195, 0.56);
|
||||||
|
--codeBgColor: #282c34;
|
||||||
|
--codeBlockBgColor: #282c34;
|
||||||
|
--footnoteBgColor: rgba(51, 51, 51, 0.5);
|
||||||
|
--inputBgColor: #3d404e;
|
||||||
|
--focusColor: #5c99f3;
|
||||||
|
|
||||||
|
/* —— 按钮 —— */
|
||||||
|
--buttonFontColor: #c3c3c3;
|
||||||
|
--buttonBgColor: #333333;
|
||||||
|
--buttonBorder: 1px solid #353a45;
|
||||||
|
--buttonShadow: none;
|
||||||
|
--buttonFontColorHover: #ffffff;
|
||||||
|
--buttonBgColorHover: #414855;
|
||||||
|
--buttonBorderHover: var(--buttonBorder);
|
||||||
|
--buttonFontColorActive: var(--buttonFontColor);
|
||||||
|
--buttonBgColorActive: #2a2d35;
|
||||||
|
--buttonBorderActive: var(--buttonBorder);
|
||||||
|
--buttonFocusBorder: 1px solid #5c99f3;
|
||||||
|
--buttonFocusShadow: 0 0 0 1px rgba(92, 153, 243, 0.3);
|
||||||
|
|
||||||
|
--buttonPrimaryFontColor: #ffffff;
|
||||||
|
--buttonPrimaryBgColor: #5c99f3;
|
||||||
|
--buttonPrimaryBorder: 1px solid #4a8ad9;
|
||||||
|
--buttonPrimaryShadow: none;
|
||||||
|
--buttonPrimaryFontColorHover: var(--buttonPrimaryFontColor);
|
||||||
|
--buttonPrimaryBgColorHover: #4a8ad9;
|
||||||
|
--buttonPrimaryBorderHover: var(--buttonPrimaryBorder);
|
||||||
|
--buttonPrimaryFontColorActive: var(--buttonPrimaryFontColor);
|
||||||
|
--buttonPrimaryBgColorActive: #3d7bc4;
|
||||||
|
--buttonPrimaryBorderActive: var(--buttonPrimaryBorder);
|
||||||
|
--buttonPrimaryFocusBorder: none;
|
||||||
|
--buttonPrimaryFocusShadow: inset 0 0 0 1px rgba(24, 26, 31, 0.5), 0 0 0 1px #5c99f3;
|
||||||
|
|
||||||
|
/* —— Markdown 元素 —— */
|
||||||
|
--headingColor: #c3c3c3;
|
||||||
|
--h1Color: #c3c3c3;
|
||||||
|
--h2Color: #c3c3c3;
|
||||||
|
--h3Color: #c3c3c3;
|
||||||
|
--h4Color: #ffffff;
|
||||||
|
--h5Color: #c3c3c3;
|
||||||
|
--h6Color: #c3c3c3;
|
||||||
|
--blockquoteTextColor: rgba(195, 195, 195, 0.8);
|
||||||
|
--blockquoteBorderColor: #5c99f3;
|
||||||
|
--hrColor: #5c99f3;
|
||||||
|
--linkColor: #e94ab9;
|
||||||
|
--strongColor: #5c99f3;
|
||||||
|
--emColor: #e94ab9;
|
||||||
|
--listMarkerColor: #5c99f3;
|
||||||
|
--tableBorderColor: #393e49;
|
||||||
|
|
||||||
|
/* —— 侧边栏 —— */
|
||||||
|
--sideBarColor: #c3c3c3;
|
||||||
|
--sideBarIconColor: var(--iconColor);
|
||||||
|
--sideBarTitleColor: #c3c3c3;
|
||||||
|
--sideBarTextColor: #7a7a7a;
|
||||||
|
--sideBarBgColor: #1f1f1e;
|
||||||
|
--sideBarItemHoverBgColor: #333333;
|
||||||
|
--itemBgColor: #333333;
|
||||||
|
|
||||||
|
/* —— 浮动层 —— */
|
||||||
|
--floatFontColor: #c3c3c3;
|
||||||
|
--floatBgColor: #353a45;
|
||||||
|
--floatHoverColor: #414855;
|
||||||
|
--floatBorderColor: #353a45;
|
||||||
|
--floatShadow: rgba(0, 0, 0, 0.3);
|
||||||
|
--maskColor: rgba(0, 0, 0, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====================================================================
|
||||||
|
* 全局背景 — 统一为 Typora onelight-dark 底色 #1f1f1e
|
||||||
|
* ==================================================================== */
|
||||||
|
html, body {
|
||||||
|
background-color: #1f1f1e;
|
||||||
|
}
|
||||||
|
.editor-wrapper {
|
||||||
|
background-color: #1f1f1e;
|
||||||
|
}
|
||||||
|
.editor-component {
|
||||||
|
background-color: #1f1f1e;
|
||||||
|
}
|
||||||
|
#ag-editor-id {
|
||||||
|
background-color: #1f1f1e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====================================================================
|
||||||
|
* 滚动条
|
||||||
|
* ==================================================================== */
|
||||||
|
::-webkit-scrollbar,
|
||||||
|
::-webkit-scrollbar-corner {
|
||||||
|
background: var(--editorBgColor);
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar:vertical { width: 8px; }
|
||||||
|
::-webkit-scrollbar:vertical:hover { width: 10px; }
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(134, 134, 134, 0.27);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====================================================================
|
||||||
|
* 弹窗 (Element UI)
|
||||||
|
* ==================================================================== */
|
||||||
|
.v-modal { background: #08090a !important; opacity: 0.65 !important; }
|
||||||
|
.el-dialog,
|
||||||
|
.el-dialog.ag-dialog-table {
|
||||||
|
border-radius: 6px !important;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3) !important;
|
||||||
|
border: 1px solid #353a45 !important;
|
||||||
|
background: #2a2a29 !important;
|
||||||
|
}
|
||||||
|
.el-button:focus { color: var(--buttonFontColor); }
|
||||||
|
.el-button--primary:focus {
|
||||||
|
color: var(--buttonPrimaryFontColor);
|
||||||
|
background: var(--buttonPrimaryBgColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====================================================================
|
||||||
|
* 标题栏 / 浮动菜单
|
||||||
|
* ==================================================================== */
|
||||||
|
.title-bar .frameless-titlebar-button > div > svg { fill: #c3c3c3; }
|
||||||
|
.title-bar .frameless-titlebar-minimize:hover,
|
||||||
|
.title-bar .frameless-titlebar-toggle:hover {
|
||||||
|
background-color: rgba(195, 195, 195, 0.05) !important;
|
||||||
|
}
|
||||||
|
.input-wrapper {
|
||||||
|
border-radius: 5px !important;
|
||||||
|
border: 1px solid #353a45 !important;
|
||||||
|
background: #2a2a29 !important;
|
||||||
|
}
|
||||||
|
.ag-front-menu,
|
||||||
|
.ag-float-wrapper {
|
||||||
|
box-shadow: 0 4px 8px 0 var(--floatShadow) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====================================================================
|
||||||
|
* 侧边栏
|
||||||
|
* ==================================================================== */
|
||||||
|
.side-bar { border-right: 1px solid #333333 !important; }
|
||||||
|
.left-column ul > li > svg { fill: rgba(195, 195, 195, 0.6) !important; }
|
||||||
|
.left-column ul > li:hover > svg { fill: #ffffff !important; }
|
||||||
|
.left-column ul > li.active > svg { fill: #5c99f3 !important; }
|
||||||
|
|
||||||
|
/* ====================================================================
|
||||||
|
* 标签栏
|
||||||
|
* ==================================================================== */
|
||||||
|
.editor-tabs {
|
||||||
|
box-shadow: none !important;
|
||||||
|
background: var(--editorBgColor) !important;
|
||||||
|
}
|
||||||
|
.editor-tabs:after {
|
||||||
|
position: absolute;
|
||||||
|
content: '';
|
||||||
|
border-bottom: 1px solid #333333;
|
||||||
|
bottom: 0; left: 0; right: 0; z-index: 1;
|
||||||
|
}
|
||||||
|
.editor-tabs ul.tabs-container:after {
|
||||||
|
position: absolute;
|
||||||
|
content: '';
|
||||||
|
border-bottom: 1px solid #333333;
|
||||||
|
bottom: 0; left: 0; right: 0; z-index: 2;
|
||||||
|
}
|
||||||
|
.tabs-container > li,
|
||||||
|
.tabs-container > li.active { background: var(--editorBgColor) !important; }
|
||||||
|
.tabs-container > li.active {
|
||||||
|
border: 1px solid #333333;
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
.tabs-container > li.active:after {
|
||||||
|
top: 0 !important; right: auto !important;
|
||||||
|
width: 2px !important; height: auto !important;
|
||||||
|
background: #5c99f3 !important;
|
||||||
|
}
|
||||||
|
.tabs-container svg.close-icon #unsaved-circle-icon { fill: #5c99f3; }
|
||||||
|
|
||||||
|
/* ====================================================================
|
||||||
|
* 编辑器内容区 (v0.19.x 旧引擎: 容器是 #ag-editor-id)
|
||||||
|
* ==================================================================== */
|
||||||
|
|
||||||
|
/* ---- 标题 ---- */
|
||||||
|
#ag-editor-id h1 {
|
||||||
|
text-align: center;
|
||||||
|
padding-bottom: 0.3em;
|
||||||
|
}
|
||||||
|
#ag-editor-id h2 {
|
||||||
|
border-bottom: 2px solid var(--themeColor);
|
||||||
|
}
|
||||||
|
#ag-editor-id h3 {
|
||||||
|
border-left: 4px solid var(--themeColor);
|
||||||
|
padding-left: 8px;
|
||||||
|
}
|
||||||
|
#ag-editor-id h4 {
|
||||||
|
color: #ffffff !important;
|
||||||
|
background: var(--themeColor);
|
||||||
|
border-radius: 5px;
|
||||||
|
width: fit-content;
|
||||||
|
padding: 2px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 粗体 / 斜体 ---- */
|
||||||
|
#ag-editor-id strong { color: var(--strongColor); font-weight: 700; }
|
||||||
|
#ag-editor-id em { color: var(--emColor); }
|
||||||
|
|
||||||
|
/* ---- 链接 ---- */
|
||||||
|
#ag-editor-id a {
|
||||||
|
color: var(--linkColor);
|
||||||
|
font-weight: 700;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 行内代码 ---- */
|
||||||
|
#ag-editor-id code {
|
||||||
|
background-color: var(--codeBlockBgColor);
|
||||||
|
color: #eb4c37;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 2px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 代码块 ---- */
|
||||||
|
#ag-editor-id :not(pre) > code[class*='language-'],
|
||||||
|
#ag-editor-id pre:not(.CodeMirror-line),
|
||||||
|
#ag-editor-id pre[class*='language-'],
|
||||||
|
#ag-editor-id pre.ag-paragraph {
|
||||||
|
background: var(--codeBlockBgColor) !important;
|
||||||
|
border: none !important;
|
||||||
|
border-radius: 5px !important;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||||
|
transition: box-shadow 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 引用块 (四面边框 + 左侧加粗) ---- */
|
||||||
|
#ag-editor-id blockquote {
|
||||||
|
padding: 10px 10px 10px 20px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
border: 2px solid var(--blockquoteBorderColor);
|
||||||
|
border-left: 5px solid var(--blockquoteBorderColor);
|
||||||
|
border-radius: 0 5px 5px 0;
|
||||||
|
background: transparent;
|
||||||
|
overflow: auto;
|
||||||
|
transition: background-color 0.3s ease, box-shadow 0.3s ease;
|
||||||
|
}
|
||||||
|
#ag-editor-id blockquote:hover {
|
||||||
|
background: var(--editorBgColor);
|
||||||
|
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
/* 隐藏引擎默认的 ::before 竖条,改用 border-left */
|
||||||
|
#ag-editor-id blockquote::before {
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 分割线 ---- */
|
||||||
|
#ag-editor-id hr {
|
||||||
|
height: 2px;
|
||||||
|
background-color: var(--hrColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 表格 ---- */
|
||||||
|
#ag-editor-id table tr th::before,
|
||||||
|
#ag-editor-id table tr td::before {
|
||||||
|
border-color: var(--tableBorderColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 图片 ---- */
|
||||||
|
#ag-editor-id img {
|
||||||
|
border-radius: 10px;
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 任务列表复选框 ---- */
|
||||||
|
#ag-editor-id li.ag-task-list-item {
|
||||||
|
list-style-type: none;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
#ag-editor-id li.ag-task-list-item > input[type='checkbox'] {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 16px; height: 16px; top: 0.1em;
|
||||||
|
margin: 0; left: -24px;
|
||||||
|
transform-origin: center;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
#ag-editor-id li.ag-task-list-item > input[type='checkbox']::before {
|
||||||
|
content: '';
|
||||||
|
width: 16px; height: 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: inline-block;
|
||||||
|
border: 2px solid var(--editorColor50);
|
||||||
|
border-radius: 2px;
|
||||||
|
background-color: var(--editorBgColor);
|
||||||
|
position: absolute; top: 0; left: 0;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
#ag-editor-id li.ag-task-list-item > input[type='checkbox']:checked::before {
|
||||||
|
border-color: transparent;
|
||||||
|
background-color: var(--themeColor);
|
||||||
|
}
|
||||||
|
#ag-editor-id li.ag-task-list-item > input[type='checkbox']::after {
|
||||||
|
content: '';
|
||||||
|
transform: rotate(-45deg) scale(0);
|
||||||
|
width: 9px; height: 5px;
|
||||||
|
border: 2px solid #fff;
|
||||||
|
border-top: none; border-right: none;
|
||||||
|
position: absolute; display: inline-block;
|
||||||
|
top: 1px; left: 5px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
#ag-editor-id li.ag-task-list-item > input[type='checkbox']:checked::after {
|
||||||
|
transform: rotate(-45deg) scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 段落悬停 ---- */
|
||||||
|
#ag-editor-id p.ag-paragraph {
|
||||||
|
padding: 5px;
|
||||||
|
margin: 0;
|
||||||
|
border-radius: 5px;
|
||||||
|
transition: background-color 0.3s ease, box-shadow 0.3s ease;
|
||||||
|
}
|
||||||
|
#ag-editor-id p.ag-paragraph:hover {
|
||||||
|
background: #141414;
|
||||||
|
box-shadow: 0 4px 4px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 已完成任务文字 ---- */
|
||||||
|
#ag-editor-id li.ag-task-list-item > input.ag-checkbox-checked ~ * {
|
||||||
|
color: var(--editorColor50);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 列表标记 ---- */
|
||||||
|
#ag-editor-id ul > li::marker,
|
||||||
|
#ag-editor-id ol > li::marker {
|
||||||
|
color: var(--listMarkerColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 搜索高亮 ---- */
|
||||||
|
.ag-highlight {
|
||||||
|
background: #ff0 !important;
|
||||||
|
color: red !important;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 0 2px;
|
||||||
|
height: auto !important;
|
||||||
|
line-height: inherit !important;
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 表格表头 ---- */
|
||||||
|
#ag-editor-id table tr th {
|
||||||
|
background: var(--editorColor04);
|
||||||
|
}
|
||||||
|
#ag-editor-id table tr:first-child td {
|
||||||
|
background: var(--editorColor04);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 脚注 ---- */
|
||||||
|
#ag-editor-id sup.md-footnote {
|
||||||
|
color: var(--strongColor);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 前置图标 ---- */
|
||||||
|
.ag-front-icon {
|
||||||
|
fill: var(--editorColor30);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ====================================================================
|
||||||
|
* CodeMirror 源码模式 (v0.19.x 旧引擎)
|
||||||
|
* ==================================================================== */
|
||||||
|
.source-code {
|
||||||
|
background: var(--editorBgColor);
|
||||||
|
}
|
||||||
|
.source-code .CodeMirror {
|
||||||
|
color: var(--editorColor);
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.source-code .CodeMirror-gutters {
|
||||||
|
background: transparent;
|
||||||
|
border-right-color: #333333;
|
||||||
|
}
|
||||||
|
.source-code .CodeMirror-linenumber {
|
||||||
|
color: var(--editorColor30);
|
||||||
|
}
|
||||||
|
.source-code .CodeMirror-cursor {
|
||||||
|
border-left-color: var(--themeColor);
|
||||||
|
}
|
||||||
|
.source-code .CodeMirror-activeline-background {
|
||||||
|
background: var(--editorColor04);
|
||||||
|
}
|
||||||
|
.source-code .CodeMirror-selected {
|
||||||
|
background: var(--selectionColor) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CodeMirror 语法高亮色 — 参考 onelight 配色 */
|
||||||
|
.cm-s-one-dark .cm-comment { color: #7f848e; font-style: italic; }
|
||||||
|
.cm-s-one-dark .cm-keyword { color: #c678dd; }
|
||||||
|
.cm-s-one-dark .cm-operator { color: #56b6c2; }
|
||||||
|
.cm-s-one-dark .cm-builtin { color: #e5c07b; }
|
||||||
|
.cm-s-one-dark .cm-atom { color: #d19a66; }
|
||||||
|
.cm-s-one-dark .cm-number { color: #d19a66; }
|
||||||
|
.cm-s-one-dark .cm-def { color: #61afef; }
|
||||||
|
.cm-s-one-dark .cm-string { color: #98c379; }
|
||||||
|
.cm-s-one-dark .cm-string-2 { color: #e06c75; }
|
||||||
|
.cm-s-one-dark .cm-variable { color: #e06c75; }
|
||||||
|
.cm-s-one-dark .cm-variable-2 { color: #e5c07b; }
|
||||||
|
.cm-s-one-dark .cm-variable-3 { color: #d19a66; }
|
||||||
|
.cm-s-one-dark .cm-type { color: #e5c07b; }
|
||||||
|
.cm-s-one-dark .cm-meta { color: #e06c75; }
|
||||||
|
.cm-s-one-dark .cm-attribute { color: #d19a66; }
|
||||||
|
.cm-s-one-dark .cm-property { color: #e06c75; }
|
||||||
|
.cm-s-one-dark .cm-qualifier { color: #e5c07b; }
|
||||||
|
.cm-s-one-dark .cm-tag { color: #e06c75; }
|
||||||
|
.cm-s-one-dark .cm-link { color: #c678dd; }
|
||||||
|
.cm-s-one-dark .cm-header { color: #61afef; }
|
||||||
|
.cm-s-one-dark .cm-quote { color: #98c379; }
|
||||||
|
|
||||||
|
/* ====================================================================
|
||||||
|
* Mermaid/图表 SVG 暗色适配
|
||||||
|
* ==================================================================== */
|
||||||
|
figure[data-role] svg rect[fill='#ffffff'] { fill: var(--editorBgColor); }
|
||||||
|
figure[data-role] svg rect[stroke='#000000'] { stroke: var(--editorColor); }
|
||||||
|
figure[data-role] svg text[fill='#000000'] { fill: var(--editorColor); }
|
||||||
|
figure[data-role] svg path[stroke='#000000'] { stroke: var(--editorColor); }
|
||||||
|
figure[data-role] svg path[fill='#ffffff'] { fill: var(--editorBgColor); }
|
||||||
|
figure[data-role] svg path[fill='#000000'] { fill: var(--editorColor); }
|
||||||
|
figure[data-role] svg use[fill='black'] { fill: var(--editorColor); }
|
||||||
|
figure[data-role] svg use[fill='#000000'] { fill: var(--editorColor); }
|
||||||
|
|
||||||
|
```
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# 正则表达式
|
||||||
|
|
||||||
|
正则表达式是一种用于匹配和操作文本的强大工具,用于描述要匹配的文本模式。
|
||||||
|
|
||||||
|
## 匹配字符
|
||||||
|
|
||||||
|
### 普通匹配字符
|
||||||
|
|
||||||
|
| 字符 | 描述 |
|
||||||
|
| :------- | :----------------------------------------------------------- |
|
||||||
|
| `[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]` | 中文字符 |
|
||||||
|
| `^1[3-9]\d{9}$` | 国内手机号 |
|
||||||
|
| `[1-9]\d{5}(?!\d)` | 国内邮政编码 |
|
||||||
|
| `^(\d{6})(\d{4})(\d{2})(\d{2})(\d{3})([0-9]|X)$` | 18位身份证号 |
|
||||||
|
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
[Typora onelight 主题](https://github.com/caolib/typora-onelight-theme?tab=readme-ov-file#4%E5%85%B3%E4%BA%8E%E5%AD%97%E4%BD%93)
|
[Typora onelight 主题](https://github.com/caolib/typora-onelight-theme?tab=readme-ov-file#4%E5%85%B3%E4%BA%8E%E5%AD%97%E4%BD%93)
|
||||||
|
|
||||||
[Typora自定义主题](https://theme.typora.io/doc/zh/Write-Custom-Theme/)
|
[Typora自定义主题](https://theme.typora.io/doc/zh/Write-Custom-Theme/)
|
||||||
|
|
||||||
|
> 通过自定义的CSS变量来实现
|
||||||
|
|
||||||
|
[CSDN增强助手](https://chromewebstore.google.com/detail/csdn%E5%A2%9E%E5%BC%BA%E5%8A%A9%E6%89%8B/fpgaealcfgjbodghojppfmohfcoajled)
|
||||||
|
|
||||||
|
> 能够智能屏蔽CSDN搜索结果、屏蔽其广告、自动展开代码
|
||||||
|
|||||||
Reference in New Issue
Block a user