[{"content":"利用 ocr 整理仓库库存 需求: 统计整理仓库里联想主机的序列号 思路:\n通过扫码枪的方式，手机扫描记录 S/N 号码再导出\n通过调用联想接口，小程序可识别部分 S/N，但机型太久远估计数据库不全面，且没有找到接口，只找到一条外链分析 S/N 号码 https://cas.wx.lenovo.com.cn/api/device/check/sn?sn=\n通过拍照上传图片，OCR 识别，可以通过天若一张张识别，或者选择百度智能云免费接口，再将图片上传到七牛云个人空间外链识别 百度智能云\n流程:\n获取图片，经测试，iPhone12mini 照片精度为 6 台机左右 统一图片格式命名，可以用批量文件软件改名，方便后续遍历 上传图片到七牛云，生成 CDN 外链 提交 post 请求到百度 API，返回识别结果保存到本地 数据清洗，将文本解析为 JSON，提取所有的 words 字段写入新文件，正则匹配所有 PC 开头，后面跟着 6 个字符，纠正识别错误\u0026rsquo;PCO\u0026rsquo;为\u0026rsquo;PC0' 后续人工校对 API 请求识别 识别后会返回一个 json 格式的 txt 文件，需要对内容进行清洗提取 展开代码 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 32 33 34 35 36 37 import requests API_KEY = \u0026#34;\u0026#34; SECRET_KEY = \u0026#34;\u0026#34; def main(): url = \u0026#34;https://aip.baidubce.com/rest/2.0/ocr/v1/accurate_basic?access_token=\u0026#34; + get_access_token() with open(\u0026#34;PCNAME2.txt\u0026#34;, \u0026#34;w+\u0026#34;, encoding=\u0026#34;utf-8\u0026#34;) as f: # 循环写入数据 for i in range(45,117): #print(i) payload=\u0026#39;\u0026#39;.format(i) headers = { \u0026#39;Content-Type\u0026#39;: \u0026#39;application/x-www-form-urlencoded\u0026#39;, \u0026#39;Accept\u0026#39;: \u0026#39;application/json\u0026#39; } response = requests.request(\u0026#34;POST\u0026#34;, url, headers=headers, data=payload) #f.write(str(i)+\u0026#39;\\n\u0026#39;) f.write(response.text+\u0026#39;\\n\u0026#39;) print(response.text) f.close() def get_access_token(): \u0026#34;\u0026#34;\u0026#34; 使用 AK，SK 生成鉴权签名（Access Token） :return: access_token，或是None(如果错误) \u0026#34;\u0026#34;\u0026#34; url = \u0026#34;https://aip.baidubce.com/oauth/2.0/token\u0026#34; params = {\u0026#34;grant_type\u0026#34;: \u0026#34;client_credentials\u0026#34;, \u0026#34;client_id\u0026#34;: API_KEY, \u0026#34;client_secret\u0026#34;: SECRET_KEY} return str(requests.post(url, params=params).json().get(\u0026#34;access_token\u0026#34;)) if __name__ == \u0026#39;__main__\u0026#39;: main() 数据清洗，封装整合 由于精度不是特别高，注意经常会有数字 0 被识别成字母 O，字母 I 和 L 被识别成 1，因此需要人工检查排除错误，另外还有一部分可能被识别漏了，因此在整理的时候按特定顺序排列方便后续找回补充数据 展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 import json import re def re_matchPC(content): # 编译正则表达式模式 # PC开头，后面跟着6个字符（字母或数字） pattern = re.compile(r\u0026#39;PC[A-Z0-9]{6}\u0026#39;) # 存储匹配的结果 matches = [] # 读取文件并查找匹配 # 找到所有匹配项 matches = pattern.findall(content) # 去除重复项 unique_matches = list(dict.fromkeys(matches)) # 将结果写入新文件 print(f\u0026#34;提取完成！共找到 {len(unique_matches)} 个唯一的PC编号，结果已保存到 new 3.txt\u0026#34;) return unique_matches def replace_O(lines): # Read the original file # Process the lines, replacing \u0026#39;PCO\u0026#39; with \u0026#39;PC0\u0026#39; modified_lines = [line.replace(\u0026#39;PCO\u0026#39;, \u0026#39;PC0\u0026#39;) for line in lines] # Write the modified lines back to the file print(\u0026#34;File has been updated successfully.\u0026#34;) return modified_lines # 读取输入文件 words_list = [] with open(\u0026#39;new 1.txt\u0026#39;, \u0026#39;r\u0026#39;, encoding=\u0026#39;utf-8\u0026#39;) as f: # 逐行读取并处理 for line in f: try: # 解析每一行的JSON json_data = json.loads(line.strip()) # 提取words_result中的words字段 if \u0026#39;words_result\u0026#39; in json_data: for item in json_data[\u0026#39;words_result\u0026#39;]: if \u0026#39;words\u0026#39; in item: words_list.append(item[\u0026#39;words\u0026#39;]) except json.JSONDecodeError: print(f\u0026#34;警告：跳过无效的JSON行: {line[:50]}...\u0026#34;) except Exception as e: print(f\u0026#34;处理行时发生错误：{str(e)}\u0026#34;) print(\u0026#39; \u0026#39;.join(words_list)) print(f\u0026#34;提取完成！共提取了 {len(words_list)} 个words，结果已保存到 new 2.txt\u0026#34;) content = \u0026#39; \u0026#39;.join(words_list) lines = re_matchPC(content) results = replace_O(lines) print(results) ","date":"2025-01-14T00:00:00Z","permalink":"https://cancanneed64.github.io/p/%E5%88%A9%E7%94%A8ocr%E6%95%B4%E7%90%86%E4%BB%93%E5%BA%93%E5%BA%93%E5%AD%98/","title":"利用ocr整理仓库库存"},{"content":"go 语言实战 1. 介绍 2. Go 包执行顺序 获取数据 -\u0026gt; 一组等待搜索的数据源 执行搜索 -\u0026gt; 使用接口进行匹配-发送结果-报告任务完成 跟踪结果 -\u0026gt; 停止结果 显示结果 main search feed match RSS 匹配器\n3. 打包和工具链 导入包查找顺序 /usr/local/go/src/pkg /home/myproject/src /home/mylibraries/s\n更改包导入名 myfmt \u0026quot;mylib/fmt\u0026quot; 导入的包一定要被使用，否则需要用 \u0026ldquo;_\u0026rdquo;\n函数 init 会在开始时被调用\ngo 命令\ngo run 运行 go 程序\ngo build 构建包打包成 exe，\u0026quot;\u0026hellip;\u0026ldquo;代表所有字符串\ngo get 导入网络包的时候会出错，需要使用 get 来获取，如果仍失败，需要下载到本地，并且将路径名改成和包名一致，本地导入\ngo get -u golang.org/x/tools/cmd/godoc import \u0026quot;./model\u0026quot;\ngo clean 删除编译生成的可执行文件\ngo doc 可查看本地文档，变成网页模式查看 go doc tar godoc -http=:6060 安装 创建目录下载包 如果创建在 program 目录下没有权限执行，因此创建在 gopath 下的 src\n1 2 3 4 cd $GOPATH/src/golang.org/x/tools/cmd/ go install ... go get -u golang.org/x/tools/cmd/godoc go install golang.org/x/tools/cmd/godoc go env 修改配置环境\n1 2 go env -w GO111MODULE=on go env -w GOPROXY=https://goproxy.cn,direct go vet 检查常见错误\n介绍 godep/gb/vender\n4. 数组/切片/映射 如果定义全局变量需要用 var 来声明，不能用:=\n数组 数组赋值的时候应该注意长度一致，否则报错 如果数组长度不确定，可以使用 \u0026hellip; 代替数组的长度 var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0} 或 balance := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}\n切片 参数包含容量和长度 slice := make([]string, 3, 5)\n创建 nil 切片 var slice []int\n声明空切片 slice := make{[int, 0]} 或 slice ：= [] int {}\n切片在赋值的时候只写长度，重新 append 时会开辟新空间，如果赋值时加上容量，append 会在原位置上修改值，影响原始数据\n映射 创建和初始化映射 dict := make(map[string]int) dict := map[string]string{\u0026quot;A\u0026quot;:\u0026quot;a\u0026quot;}\n使用映射字面量声明空映射 dict := map[[] string] int{} 报错 创建一个映射，使用字符串切片做为值 dict := map[int][]string{}\n关于 map\n变量名以小写开头不公开，其他包可以间接调用，一个函数可以返回一个未公开类型的值\nmap 需要先用 make 构造，否则会报错，因为 map 变量默认零值 nil\n5. 类型 类\n1 2 3 4 type user struct{ } var bill user 方法\n1 2 3 func (u user) notify(){ } 传值，传指针，传地址\n切片/映射/通道/接口/函数\n方法传值与传指地址区别 直接传是传递副本，*表示传引用，\u0026amp;表示传地址 方法是将参数的副本做操作，如果要改变参数值，需要将参数地址传到函数\n数组是值传递，切片是引用传递\n传值 go 语言中基本数据类型、结构体、数组都是传值\n将变量的副本传递给形参 传递给函数的参数是被传数据的的副本，因此对形参（也就副本）的值进行更改操作时，并不会影响原始数据 同时，由于传值时，需要拷贝一份数据给形参，因此比较耗费资源。\n传引用 将变量的地址复制一份传递给形参，它也可以看作传值，只不过传的是地址值的副本\n传地址\n传值传引用与传地址\n知识点\n多态指定义了一个接口类型后，可以定义其他方法来实现改写改类型 接口是声明了一组行为并支持多态的类型 struct 或者通过指定已存在的类型声明用户定义的类型 类型本质 给类型增加删除某个值，是创建新值还是改变当前值，创建新值该类型方法就是值接收者，修改当前值就是指针接收者，同时影响内部传递类型值的方式 内置类型 go 语言中类型的默认值。如下：\n数值类型(如 int8、int16、uint 等)，默认值为 0 布尔类型，默认值为 false 字符串类型，默认值为\u0026rdquo;\u0026quot; 指针、通道、切片、字典等，默认值为 nil 复合类型的默认值，为所包含类型的默认值\n引用类型 切片/映射/通道/接口/函数，创建的变量为标头值\n嵌入关系 值 方法接收方 T 类型包含 (t T) *T 类型包含 (t T) 和 (t *T) 方法接收方 值 (t T) T 和*T (t *T) *T\n类型 S 包含匿名字段 T，则 S 和S 方法集包含 T 方法 类型 S 包含匿名字段T，则 S 和*S 方法集包含 T + T 方法 不管嵌入的是 T 还是T，*S 方法集包含 T + *T 方法\n当 user 定义一个用户类型，方法实现一个通过 user 类型值的指针，admin 包含 user 类型，admin 可以调用 user 内部的方法，提升到外部类型，当 admin 也实现了该方法，则不会调用 user 方法，可以通过直接访问 user 方法\n未公开标识符 定义类型，小写开头为未公开标识符 文件夹应该同包名相同 当需要调用未公开标识符时，可以创建一个工厂函数 New 来返回该未公开标识符 6. 并发 7. 并发模式 8. 标准库 log 包 json 编码解码 Writer 和 Reader 9. 测试和性能 单元测试 使用 go test -v(提供冗余输出)，没有加上除非失败，否则看不到测试输出 go 语言约定，_test.go 结尾的文件是测试文件，测试函数需要用 Test 开头，函数的签名必须接收一个指向 testing.T 类型的指针并且不返回任何值，\n表组测试 建立多个值传入测试，查看不同\n模仿调用 写一个 mockServer 模拟服务器返回\n基准测试 使用 strconv 和 testing 包，文件名需要以_test.go 结尾\n1 2 3 4 5 6 7 func BenchmarkSprintf(b *testing.B){ number := 10 b.ResetTimer() for i := 0; i \u0026lt;b.N; i++{ fmt.Sprintf(\u0026#34;%d\u0026#34;, number) } } go test -v -run=\u0026ldquo;none\u0026rdquo; -bench=\u0026ldquo;BenchmarkSprintf\u0026rdquo;\n-run 传递 none 保证运行基准测试前没有单元测试被运行 b.ResetTimer()初始化计时器 -benchmem 提供每次操作分配内存的次数，以及总共分配内存的字节数，allocs/op\n","date":"2025-01-08T22:10:45+08:00","permalink":"https://cancanneed64.github.io/p/go-%E8%AF%AD%E8%A8%80%E5%AE%9E%E6%88%98/","title":"Go 语言实战"},{"content":"Go 语言入门 1. 运行环境安装 中文网\n安装 VSCODE 后进行环境变量设置代理\n教程\n示例\n1 2 3 4 5 6 7 8 9 10 11 package main // 程序包名称 import \u0026#34;fmt\u0026#34; //main函数 func main(){ fmt.Println(\u0026#34;halo go\u0026#34;) } 终端\n1 2 3 4 5 go run hello.go go build hello.go //生成二进制文件 ./hello.go //生成二进制文件 注意点\n分号结尾不影响使用 函数的{和函数名同一行不然报错 注释用//和/* */ fmt 中 Println 空一行，Printf 表示格式化输出，%s 表示原字符，%T 表示类型，Sprintf(格式，变量) 2. 基础语法 无效标识符\n以数字开头 Go 语言关键字 运算符 导入包\n方法名需要首字母大写，否则只能在包内调用 导入包不使用会报错，前面加上_无法调用会执行内部 init 方法，起别名可直接调用，加上.可以调用全部方法 变量声明\n已被声明的变量不能再次声明 _被用来抛弃值 \u0026amp;返回地址，*指针变量 全局变量只能用普通方法声明，常用:= 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package main import ( \u0026#34;fmt\u0026#34; ) // 声明变量四种方式 var gA int = 100 func main() { //声明全局变量前三种没问题，第四种只能在函数体内声明 var a int //默认值0 fmt.Println(\u0026#34;a = \u0026#34;, a) fmt.Printf(\u0026#34;a = %T\\n\u0026#34;, a) var b int = 100 fmt.Println(\u0026#34;b = \u0026#34;, b) fmt.Printf(\u0026#34;b = %T\\n\u0026#34;, b) //不声明类型直接赋值 var c = 100 fmt.Println(\u0026#34;c = \u0026#34;, c) fmt.Printf(\u0026#34;c = %T\\n\u0026#34;, c) var d string = \u0026#34;abcd\u0026#34; fmt.Println(\u0026#34;d = \u0026#34;, d) fmt.Printf(\u0026#34;d = %T\\n\u0026#34;, d) //先初始化在赋值 e := 100 //fmt.Printf(\u0026#34;e = %s, type of e = %T\\n\u0026#34;, e, e) fmt.Println(\u0026#34;e = \u0026#34;, e) fmt.Printf(\u0026#34;e = %T\\n\u0026#34;, e) fmt.Println(\u0026#34;gA = \u0026#34;, gA) //声明多个变量 var xx, yy int = 100, 200 fmt.Println(\u0026#34;xx = \u0026#34;, xx, \u0026#34;, yy = \u0026#34;, yy) var ( aa = 100 bb = 200 ) fmt.Println(\u0026#34;aa = \u0026#34;, aa, \u0026#34;, bb = \u0026#34;, bb) } 数据类型\n常量\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package main import ( \u0026#34;fmt\u0026#34; ) const ( //const里通过iota，每行累加1，第一行默认0, 枚举 Beijing = iota //0 Shanghai Shenzhen ) func main() { //常量(只读) const length int = 10 fmt.Println(\u0026#34;length = \u0026#34;, length) fmt.Println(\u0026#34;Beijing = \u0026#34;, Beijing) fmt.Println(\u0026#34;Shanghai = \u0026#34;, Shanghai) fmt.Println(\u0026#34;Shenzhen = \u0026#34;, Shenzhen) } 多个返回值，形参\n1 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 package main import ( \u0026#34;fmt\u0026#34; ) // 匿名形参 func foo1(a string, b int) (int, int) { fmt.Println(\u0026#34;a = \u0026#34;, a) fmt.Println(\u0026#34;b = \u0026#34;, b) return 666, 777 } // 有名形参，可以写成（r1, r2 int) func foo2(a string, b int) (r1 int, r2 int) { fmt.Println(\u0026#34;a = \u0026#34;, a) fmt.Println(\u0026#34;b = \u0026#34;, b) r1 = 1000 r2 = 2000 return } func main() { ret1, ret2 := foo1(\u0026#34;haha\u0026#34;, 999) fmt.Println(\u0026#34;ret1 = \u0026#34;, ret1, \u0026#34;ret2 = \u0026#34;, ret2) ret1, ret2 = foo2(\u0026#34;foo2\u0026#34;, 222) fmt.Println(\u0026#34;ret1 = \u0026#34;, ret1, \u0026#34;ret2 = \u0026#34;, ret2) } 指针\n条件判断补充\nswitch 自带 break，继续执行 case 加 fallthrough，default 默认运行\nselect\ndefer 函数\n相当于 final，函数结尾执行，以压栈的方式先入后出 defer 和 main，return 函数比 defer 先执行 数组\n固定大小，传参传值\n1 var myArray1 [10]int 动态数组传参传指针，引用\n1 myArray ：= []int{1,2,3,4} 切片四种方式，声明切片但是不分配空间\n1 2 3 4 5 6 7 slice1 := []int{1,2,3} var slice1 []int \\\\ slice1 = make([int,3]) var slice []int = make([]int,3) slice1 := make([]int, 3) nil 空\nlen 长度，cap 容量\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package main import \u0026#34;fmt\u0026#34; func main() { s := []int{1, 2, 3} s1 := s[0:2]\t//S和s1指向同个空间 s1[0] = 100 fmt.Println(s) //[100 2 3] fmt.Println(s1)\t//[100 2] s2 := make([]int, 3) copy(s2, s)\t//s2复制s指向新的空间 fmt.Println(s2)\t//[100 2 3] s2[0] = 1000 fmt.Println(s) fmt.Println(s1) fmt.Println(s2) } map\nmap3 种声明方式\nmap 的方法\n1 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 32 33 34 35 36 37 38 package main import \u0026#34;fmt\u0026#34; func printMap(cityMap map[string]string) { //cityMap引用传递 for key, value := range cityMap { fmt.Println(\u0026#34;key = \u0026#34;, key) fmt.Println(\u0026#34;value = \u0026#34;, value) } } func ChangeValue(cityMap map[string]string) { cityMap[\u0026#34;England\u0026#34;] = \u0026#34;London\u0026#34; } func main() { cityMap := make(map[string]string) //添加 cityMap[\u0026#34;China\u0026#34;] = \u0026#34;Beijing\u0026#34; cityMap[\u0026#34;Japan\u0026#34;] = \u0026#34;Tokyo\u0026#34; cityMap[\u0026#34;USA\u0026#34;] = \u0026#34;NewYork\u0026#34; //遍历 printMap(cityMap) //删除 delete(cityMap, \u0026#34;China\u0026#34;) //修改 ChangeValue(cityMap) fmt.Println(\u0026#34;------\u0026#34;) //遍历 printMap(cityMap) } 结构体\n1 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 package main import \u0026#34;fmt\u0026#34; type Book struct { title string auth string } func changeBook(book Book) { //指针传递 book.auth = \u0026#34;666\u0026#34; } func changeBook2(book *Book) { //指针传递 book.auth = \u0026#34;777\u0026#34; } func main() { var book1 Book book1.title = \u0026#34;Golang\u0026#34; book1.auth = \u0026#34;zhuang3\u0026#34; fmt.Printf(\u0026#34;%v\\n\u0026#34;, book1) changeBook(book1) fmt.Printf(\u0026#34;%v\\n\u0026#34;, book1) changeBook2(\u0026amp;book1) fmt.Printf(\u0026#34;%v\\n\u0026#34;, book1) } 类方法\n大写字母开头的方法是公有，小写是私有\n类定义的方法 func(副本 *结构体) 类方法名（）类型{}\n1 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 32 33 package main import \u0026#34;fmt\u0026#34; type Hero struct { Name string Ad int level int //私有变量 } func (this *Hero) Show() { fmt.Println(\u0026#34;Name= \u0026#34;, this.Name) fmt.Println(\u0026#34;Ad= \u0026#34;, this.Ad) fmt.Println(\u0026#34;Level= \u0026#34;, this.level) } func (this *Hero) GetName() string { return this.Name } func (this *Hero) SetName(newName string) { this.Name = newName } func main() { hero := Hero{Name: \u0026#34;zhang3\u0026#34;, Ad: 100, level: 1} hero.Show() hero.SetName(\u0026#34;li4\u0026#34;) hero.Show() } 父类与子类 继承\n1 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package main import \u0026#34;fmt\u0026#34; type Hero struct { name string sex string } func (this *Hero) Show() { fmt.Println(\u0026#34;name= \u0026#34;, this.name) } func (this *Hero) Getname() string { return this.name } func (this *Hero) Setname(newname string) { this.name = newname } type SuperMan struct { Hero //继承Hero类方法 level int } func (this *SuperMan) Eat() { fmt.Println(\u0026#34;SuperMan.Eat()...\u0026#34;) } func (this *SuperMan) Fly() { fmt.Println(\u0026#34;SuperMan.Fly()...\u0026#34;) } func (this *SuperMan) Print() { fmt.Println(\u0026#34;name= \u0026#34;, this.name) fmt.Println(\u0026#34;sex= \u0026#34;, this.sex) fmt.Println(\u0026#34;level= \u0026#34;, this.level) } func main() { hero := Hero{\u0026#34;zhang3\u0026#34;, \u0026#34;female\u0026#34;} hero.Show() hero.Setname(\u0026#34;li4\u0026#34;) hero.Show() //s := SuperMan{Hero{\u0026#34;li4\u0026#34;, \u0026#34;female\u0026#34;}, 88}\t//容易重复 var s SuperMan s.name = \u0026#34;li4\u0026#34; s.sex = \u0026#34;male\u0026#34; s.level = 88 s.Show() s.Fly() s.Print() } 多态与接口\n1 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 package main import \u0026#34;fmt\u0026#34; type AnimalIF interface { //父类接口 Sleep() GetColor() string GetType() string } type Cat struct { //子类 color string } func (this *Cat) Sleep() { //定义父类方法 fmt.Println(\u0026#34;Cat is Sleep\u0026#34;) } func (this *Cat) GetColor() string { //报错返回值过多 return this.color } func (this *Cat) GetType() string { return \u0026#34;Cat\u0026#34; } type Dog struct { color string } func (this *Dog) Sleep() { fmt.Println(\u0026#34;Dog is Sleep\u0026#34;) } func (this *Dog) GetColor() string { return this.color } func (this *Dog) GetType() string { return \u0026#34;Dog\u0026#34; } func showAnimal(animal AnimalIF) { animal.Sleep() fmt.Println(\u0026#34;color = \u0026#34;, animal.GetColor()) // fmt.Println(\u0026#34;type = \u0026#34;, animal.GetType()) } func main() { // var animal AnimalIF // animal = \u0026amp;Cat{\u0026#34;Green\u0026#34;}\t赋值，父类接口指针指向子类的具体对象，再用父类调用接口 // animal.Sleep() // animal = \u0026amp;Dog{\u0026#34;Yellow\u0026#34;} // animal.Sleep() cat := Cat{\u0026#34;Green\u0026#34;} dog := Dog{\u0026#34;Yellow\u0026#34;} showAnimal(\u0026amp;cat) showAnimal(\u0026amp;dog) } 万能通用接口\n1 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 package main import \u0026#34;fmt\u0026#34; func myFunc(arg interface{}) { //通用万能类型 空接口 fmt.Println(\u0026#34;myFunc is called\u0026#34;) fmt.Println(arg) //interface{}区分底层数据类型，可以用类型断言arg.(string) value, ok := arg.(string) if !ok { fmt.Println(\u0026#34;arg is not string type\u0026#34;) } else { fmt.Println(\u0026#34;arg is string type, value = \u0026#34;, value) fmt.Printf(\u0026#34;value type is %T\\n\u0026#34;, value) } } type Book struct { auth string } func main() { book := Book{\u0026#34;Golang\u0026#34;} myFunc(book) myFunc(100) myFunc(\u0026#34;abc\u0026#34;) } 断言\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package main import ( \u0026#34;fmt\u0026#34; ) func main() { var a int //pair\u0026lt;statictype:string, value:\u0026#34;aceld\u0026#34;\u0026gt; // a = \u0026#34;aceld\u0026#34; a = 123 //pair\u0026lt;type:string, value:\u0026#34;aceld\u0026#34;\u0026gt; var allType interface{} allType = a str, st := allType.(string) fmt.Println(str) fmt.Println(st) } reflect\n1 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package main import ( \u0026#34;fmt\u0026#34; \u0026#34;reflect\u0026#34; ) type User struct { Id int Name string Age int } func (this User) Call() { fmt.Println(\u0026#34;user is called\u0026#34;) fmt.Printf(\u0026#34;%v\\n\u0026#34;, this) } func main() { user := User{1, \u0026#34;Aceld\u0026#34;, 18} DoFileAndMethod(user) } func DoFileAndMethod(input interface{}) { //获取input的type inputType := reflect.TypeOf(input) fmt.Println(\u0026#34;inputType is :\u0026#34;, inputType.Name()) //获取input的value inputValue := reflect.ValueOf(input) fmt.Println(\u0026#34;inputValue is :\u0026#34;, inputValue) //通过type 获取里面的字段 //1. 获取interface的reflect.Type, 通过Type得到NumField，进行遍历 //2. 得到每个filed, 数据类型 //3. 通过 filed有一个Interface() 方法等到对应的value //通过type 获取里面的方法，调用 for i := 0; i \u0026lt; inputType.NumField(); i++ { field := inputType.Field(i) value := inputValue.Field(i).Interface() fmt.Printf(\u0026#34;%s: %v = %v\\n\u0026#34;, field.Name, field.Type, value) } //通过type 获取里面的方法，调用 for i := 0; i \u0026lt; inputType.NumMethod(); i++ { m := inputType.Method(i) fmt.Printf(\u0026#34;%s: %v\\n\u0026#34;, m.Name, m.Type) } } reflect 中的 tag 标签\n","date":"2025-01-06T21:35:04+08:00","permalink":"https://cancanneed64.github.io/p/go%E8%AF%AD%E8%A8%80%E5%85%A5%E9%97%A8/","title":"Go语言入门"},{"content":"ShredOS+Powershell 处理格式化日志 ShredOS 需求: 格式化旧硬盘并整理文件统计数量 Github link\n一款开源的硬盘格式化处理软件，可以用来对 PC/Laptop/服务器等带硬盘的机器进行格式化处理，但目前无法对 HP 服务器进行很好的兼容，无法打开镜像，详情见 github issue 页面\n镜像制作过程可以参考下 github 说明，此处不作赘述\nTODO: 补充对硬盘的处理，具体怎么将配置固定\nv0.37 版本新增了对默认方式的修改，具体可以修改镜像中的 grub.cf 文件，参数说明\n参考如下\n1 2 3 4 5 6 7 8 9 10 11 set default=\u0026#34;0\u0026#34; set timeout=\u0026#34;0\u0026#34; menuentry \u0026#34;shredos\u0026#34; { linux /boot/shredos console=tty3 loglevel=3 nwipe_options=\u0026#34;--prng=isaac64 --method=dodshort --autonuke --exclude=/dev/sda\u0026#34; } location: (USB) \\boot\\grub\\grub.cfg (USB）\\EFI\\BOOT\\grub.cfg Powershell CMD 调用 Powershell 文件 powershell -ExecutionPolicy Bypass -File xx.ps1\n思路:\n打开 ShredOS 格式化处理后的 TXT 文件 按照固定行列索引的方式进行定位，找到长度为 14 的硬盘序列号，接着找到 PC 的 S/N 号码 在目录下查找到包含硬盘序列号的 PDF 文件 重命名本 TXT 文件以及找到的 PDF 文件，加上 PC 的 S/N 号码 展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 # 设置要查找的目录路径 $directoryPath = \u0026#34;Path\u0026#34; # 获取所有 TXT 文件 $txtFiles = Get-ChildItem -Path $directoryPath -Filter *.txt # 循环遍历每个文件 foreach ($file in $txtFiles) { # 输出文件名和完整路径 Write-Output \u0026#34;文件名: $($file.Name)\u0026#34; #Write-Output \u0026#34;完整路径: $($file.FullName)\u0026#34; # 读取文件内容 $content = Get-Content -Path $file.FullName # 检查文件是否包含至少 17 行 if ($content.Length -ge 17) { # 获取第 17 行 $line = $content[16] # 索引从 0 开始 # 检查行长度是否足够 if ($line.Length -ge 116) { # 103 列是索引 102 # 复制从第 103 列开始的 14 位字符 $result = $line.Substring(102, 14) Write-Output $result } else { Write-Output \u0026#34;第 17 行字符长度不足。\u0026#34; } } else { Write-Output \u0026#34;文件行数不足 17 行。\u0026#34; } # 检查文件是否包含至少 25 行 if ($content.Length -ge 25) { # 获取第 25 行 $line = $content[24] # 索引从 0 开始 # 检查行长度是否足够 if ($line.Length -ge 62) { # 63 列是索引 62 # 复制从第 55 列开始的 8 位字符 $result2 = $line.Substring(54, 8) Write-Output $result2 } else { Write-Output \u0026#34;第 25 行字符长度不足。\u0026#34; } } else { Write-Output \u0026#34;文件行数不足 25 行。\u0026#34; } # 查找文件名包含指定字符串的文件 $matchingFiles = Get-ChildItem -Path $directoryPath -File | Where-Object { $_.Name -like \u0026#34;*$result*\u0026#34; } # 输出匹配的文件 if ($matchingFiles) { foreach ($file in $matchingFiles) { Write-Output \u0026#34;找到文件: $($file.FullName)\u0026#34; } } else { Write-Output \u0026#34;未找到\u0026#34; } $newName = $result2 -join \u0026#34;_\u0026#34; -join $matchingFiles $newName2 = $result2 -join \u0026#34;_\u0026#34; -join $file.Name Write-Output $newName Write-Output $newName2 #Rename-Item $matchingFiles $newName #Rename-Item $file.Name $newName2 } 改良思路:\n将 ps1 文件放在文件夹同个目录下进行获取当前路径\n将固定行列序号改为正则匹配，规则为“S/N=”后换行符前的字符串以及“system-serial-number =”后的 8 位字符\n用 array 对 textfile 进行追加统计无法找到的 pdf 数量 展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 # 设置要查找的目录路径 $directoryPath = Get-Location # 获取所有 TXT 文件 $txtFiles = Get-ChildItem -Path $directoryPath -Filter *.txt #$regex = [regex] \u0026#34;S/N=(.*?)(\\r?\\n)\u0026#34; $regex = \u0026#39;S/N=([^ \\n]+)\u0026#39; $regex2 = [regex] \u0026#34;system-serial-number = (\\S{8})\u0026#34; $array = @(\u0026#34;以下txt文件找不到对于PDF:\u0026#34;) # 1.循环遍历每个文件 foreach ($file in $txtFiles) { Write-Output \u0026#34;---------------------------------------------------------\u0026#34; # 输出文件名和完整路径 #Write-Output \u0026#34;file.Name: $($file.Name)\u0026#34; if ($file.Name){ $textfile = $file.Name #Write-Output $textfile } # 2.读取文件内容 $content = Get-Content -Path $file.FullName -ErrorAction SilentlyContinue # 3.查找硬盘号 #$match = $regex.Match($content) $matches = [regex]::Matches($content, $regex) if ($matches.Count -ge 2) { # 获取第二个匹配项 $diskNumber = $matches[1].Groups[1].Value.Trim() #Write-Output \u0026#34;第二个 S/N: $diskNumber\u0026#34; } else { Write-Output \u0026#34;未找到第二个 S/N\u0026#34; } # 3.查找所有包含主机名 $match2 = $regex2.Match($content) if ($match2.Success) { $pcNumber = $match2.Groups[1].Value #Write-Output \u0026#34;pcNumber: $pcNumber\u0026#34; } # 4.查找文件名包含指定字符串的文件 $matchingFiles = Get-ChildItem -Path $directoryPath -File | Where-Object { $_.Name -like \u0026#34;*$diskNumber*\u0026#34; } #Write-Output $matchingFiles # 5.输出匹配的文件 if ($matchingFiles) { foreach ($file in $matchingFiles) { try{ #Write-Output \u0026#34;file.FullName: $($file.FullName)\u0026#34; $newName = $pcNumber + \u0026#34;_\u0026#34;+ $matchingFiles #Write-Output $newName Rename-Item $matchingFiles $newName -ErrorAction Stop #Write-Output \u0026#34;textName: $($textfile)\u0026#34; $newName2 = $pcNumber+ \u0026#34;_\u0026#34; + $textfile #Write-Output $newName2 Rename-Item $textfile $newName2 -ErrorAction Stop Write-Output \u0026#34;rename\u0026#34; Write-Output \u0026#34;---------------------------------------------------------\u0026#34; }catch { Write-Output \u0026#34;$pcNumber 重命名文件时出错: $_\u0026#34; } } }else { Write-Output \u0026#34;can not found PDF\u0026#34; $array += $textfile Write-Output \u0026#34;---------------------------------------------------------\u0026#34; } } Write-Output $array 目前存在问题:\n当文件出现重复名称的时候无法进行下一步处理\n无法统计改名失败的文件\n更多的容错判断\n","date":"2024-11-19T00:00:00Z","permalink":"https://cancanneed64.github.io/p/shredos-powershell%E5%A4%84%E7%90%86%E6%A0%BC%E5%BC%8F%E5%8C%96%E6%97%A5%E5%BF%97/","title":"ShredOS+Powershell处理格式化日志"},{"content":"git 1. 安装 参考链接 关闭SSL认证 找到.gitconfig文件\n1 2 3 4 [http] sslverify = false [url “https://”] insteadOf = git:// 2. 配置 1 2 3 4 5 6 名称 `git config --global user.namer \u0026#34;username\u0026#34;` 邮箱 `git config --global user.email \u0026#34;email\u0026#34;` 自动颜色 `git config --global color.ui auto` 可以在.gitconfig文件中查看\n1 2 3 4 5 6 7 8 9 10 11 [user] namer = email = [color] ui = auto [http] sslverify = false [url \u0026#34;https://\u0026#34;] insteadOf = git:// [init] defaultBranch = main 3. SSH-key 1 2 3 4 5 6 7 8 9 10 11 设置私钥密码 `ssh-keygen -t rsa -C \u0026#34;email` \u0026gt;第一个输入回车，接着输入fingerprint密码 回到github页面设置SSH ![github ssh.jpg](../_resources/github%20ssh.jpg) key中填入返回的结果 `cat ~/.ssh/id_rsa.pub` 然后输入即可生效 `ssh -T git@github.com` 4. 仓库一般操作 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 克隆仓库 `git clone git@github.com:cancanneed64/test.git` github需先创建仓库 本地创建仓库并初始化 `mkdir test ` `git init` 查看状态 `git status` 暂存区添加 `git add` 添加注释并提交 `git commit -m \u0026#34;\u0026#34;` \u0026gt; 如果直接git commit会打开编辑器，如果直接关闭会中止 查看日志 `git log` 简述多个 `git log --pretty=short` 前后差别 `git log -p` 查看工作树和暂存区的差别 `git diff` 最新一次提交 `git diff head` ## 5. 分支操作 显示分支 `git branch` 切换分支（-代替上一个分支） `git checkout master` 创建切换分支 `git checkout -b feature-A` 合并分支 `git merge` 记录合并 `git merge --no-ff feature-A` 图表形式查看分支 `git log --graph` 恢复分支节点状态 `git reset --hard ` 查看当前仓库执行过的操作日志，git log只能看以当前状态为终点的日志 `git reflog` 解决冲突示范\n1 2 3 4 5 6 git checkout master git reset --hard git merge --no-ff fix-B 修改内容 git add git commit -m \u0026#34;\u0026#34; 修改上一条信息 git commit --amend\n压缩历史，合并上一个提交 git rebase -i\nHEAD-2选定当前分支中包含HEAD在内的两个最新历史记录为对象在编辑器中打开\n6.仓库推拉 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 添加远程仓库，设置为origin `git remote add origin git@github.com:id/file.git` 本地仓库内容推送到远程仓库 `git push -u origin master` 获取远程仓库，自动设置为origin，本地与仓库master分支相同 `git clone git@github.com:id/file.git` 查看远程和本地的分支 `git branch -a` 以名为origin 的仓库（这里指GitHub 端的仓库）的feature-D 分 支为来源，在本地仓库中创建feature-D 分支 `git checkout -b feature-D origin/feature-D` 从远程仓库拉取 `git pull origin feature-D` 7. Pull request 1 2 3 4 5 给原仓库设置名称 `git remote add upstream git://github.com/file.git` 从远程仓库获取最新代码，将upstream/master 分支与当前分（master）合并 `git fetch upstream` error git push origin master remote: error: GH007: Your push would publish a private email address.\nKeep my email addresses private 8. 一般操作 …or create a new repository on the command line\n1 2 3 4 5 6 7 echo \u0026#34;# sample\u0026#34; \u0026gt;\u0026gt; README.md git init git add README.md git commit -m \u0026#34;first commit\u0026#34; git branch -M main git remote add origin https://github.com/melodyka/sample.git git push -u origin main …or push an existing repository from the command line\n1 2 3 git remote add origin https://github.com/melodyka/sample.git git branch -M main git push -u origin main …or import code from another repository\n1 You can initialize this repository with code from a Subversion, Mercurial, or TFS project. ! [rejected] main -\u0026gt; main (fetch first) error: failed to push some refs to\n9. 其他操作 撤回未推送最新一次提交 git reset HEAD^ 或 直接删除git重新开始\n切换仓库 git remote set-url origin URL 或git remote rm origin git remote add origin url\n删除仓库内容\n1 2 3 4 git rm * -f -r#删除所有文件夹包括文件 git add . git commit -m \u0026#34;***\u0026#34; #增加提交信息 git push origin master#master是远程分支 ","date":"2024-10-03T00:00:00Z","permalink":"https://cancanneed64.github.io/p/git%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4/","title":"Git常用命令"},{"content":"表格索引多种查询方法 需求: 有时候会遇到在固定表格里面索引的需求，然而这些表格的格式并不统一，需要进行处理后再索引，因此有了这些方案\n转换表格为数据 思路一，py 转 pdf 为 excel 展开代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import pdfplumber from openpyxl import Workbook def convert_pdf_to_excel(pdf_path, excel_path): pdf = pdfplumber.open(pdf_path) wb = Workbook() ws = wb.active for page in pdf.pages: table = page.extract_tables()[0] # 提取第一个表格 for row in table: ws.append(row) pdf.close() wb.save(excel_path) wb.close() # 示例用法 pdf_path = r\u0026#39;grid.pdf\u0026#39; excel_path =r\u0026#39;output.xlsx\u0026#39; convert_pdf_to_excel(pdf_path, excel_path) 思路二，将图片转为表格，ocr 识别 展开代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import pytesseract from PIL import Image pytesseract.pytesseract.tesseract_cmd = r\u0026#39;Tesseract-OCR\u0026#39; path = \u0026#34;\u0026#34; # 加载图片 image_path = \u0026#39;image.png\u0026#39; # 图片文件路径 image = Image.open(path+\u0026#39;\\\\\u0026#39;+image_path) # 使用OCR识别图片中的字符串 text = pytesseract.image_to_string(image) # 打印识别结果 print(text) 思路三，读取 csv 文件，并用 pandas 索引 展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 import csv import os import pandas as pd # 读取CSV文件并获取指定列和行的数据 def read_csv(filename): data = [] with open(filename, \u0026#39;r\u0026#39;) as file: reader = csv.reader(file) header = next(reader) # 读取CSV文件的标题行 for row in reader: for element in row: #\u0026#39;1 6 N K J V 9 9 Q N 9 V 8\u0026#39; cleaned_row = element.replace(\u0026#34; \u0026#34;, \u0026#34;\u0026#34;) #for i in cleaned_row: #cleaned_row = \u0026#34;,\u0026#34;.join(cleaned_row) data.append(cleaned_row) #header = [\u0026#34;\u0026#34;.join(header[0].replace(\u0026#34; \u0026#34;, \u0026#34;\u0026#34;))] header = [header[0].replace(\u0026#34; \u0026#34;, \u0026#34;\u0026#34;)] hh =[\u0026#39;N\u0026#39;] for h in header[0]: hh.append(h) #print(hh) l = [[0] * 13 for j in range(7)] #print(l) for i in range(7): for j in range(13): l[i][j] = (data[i][j]) return l,hh # 示例用法 filename = \u0026#39;grid.csv\u0026#39; # CSV文件路径 #columns = [N A B C D E F G H I J K L] # 需要获取的列名称列表 path = \u0026#34;\u0026#34; data,header = read_csv(path + \u0026#34;\\\\\u0026#34; + filename) index = [1,2,3,4,5,6,7] df = pd.DataFrame(data,index=index,columns=header) df = df.drop(df.columns[0],axis=1) print(df) m = \u0026#39;A\u0026#39; n = 3 ci = df.columns.get_loc(m) ri = df.index.get_loc(n) print(df.iloc[ri,ci]) 思路四，读取 pdf 并打印结果 展开代码 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 import pandas as pd import pdfplumber import sys #import getpass def extract_table_from_pdf(file_path): with pdfplumber.open(file_path) as pdf: table = pdf.pages[0].extract_tables()[0] return table try: file = \u0026#34;\u0026#34; table = extract_table_from_pdf(file) index = [1,2,3,4,5,6,7] df = pd.DataFrame(table[1:], columns=table[0], index=index) #print(df) ip = sys.stdin.read().rstrip(\u0026#39;\\n\u0026#39;) ip_list = ip.splitlines() ip_list = [ip for ip in ip_list if ip.strip()] for i in range(len(ip_list)): #print(ip_list[i][0],ip_list[i][1]) ci = df.columns.get_loc(ip_list[i][0]) ri = df.index.get_loc(int(ip_list[i][1])) #print(ci,ri) #res.append(df.iloc[ri,ci]) print(df.iloc[ri,ci]) #print(res) except Exception as e: print(f\u0026#34;An error occurred: {e}\u0026#34;) Excel 表格处理 优点: 不需要下载工具上传数据，使用便捷，基本都有 excel 和 adobe 缺点: 需要手工操作，公式设置复杂 用 Adobe Reader 打开 pdf 表格，将表格复制到 excel 中，用分列进行初始化排列好顺序 =INDEX(表格区域,MATCH(INT(RIGHT(目标索引,1)),列),MATCH(LEFT(目标索引,1),行,0))\nPython 制作本地小工具 优点: 不用再设置公式，本地工具方便操作 缺点: 依赖工具，后续改进需要重新编译 Python 图形化界面模板 展开代码 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 32 33 34 35 36 from tkinter import * import time from tkinter import filedialog import ctypes import tkinter as tk def run1(): print(1) # 创建主窗口 root = tk.Tk() # 设置窗口标题 root.title(\u0026#34;简单GUI界面\u0026#34;) root.geometry(\u0026#39;460x300\u0026#39;) txt1 = Text(root) txt1.place(relx=0.1, rely=0.2, relwidth=0.2, relheight=0.4) txt2 = Text(root) txt2.place(relx=0.6, rely=0.2, relwidth=0.2, relheight=0.4) btn1 = Button(root,text =\u0026#39;提交\u0026#39;,command=lambda:run1(txt1.get(\u0026#39;0.0\u0026#39;,\u0026#39;end\u0026#39;))) btn1.place(relx=0.4, rely=0.3, relwidth=0.1, relheight=0.1) myappid = \u0026#34;company.product.version\u0026#34; # 这里可以设置任意文本 ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid) root.wm_iconbitmap(\u0026#39;favicon.ico\u0026#39;) # 设置程序图标 # 创建按钮点击事件处理函数 def button_click(): label.config(text=\u0026#34;按钮被点击了！\u0026#34;) # 运行主循环 root.mainloop() pandas 序列化图形化处理 展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 from tkinter import * import time import tkinter as tk import ctypes import pandas as pd import pdfplumber def extract_table_from_pdf(file_path): with pdfplumber.open(file_path) as pdf: table = pdf.pages[0].extract_tables()[0] return table def run1(ip): try: if txt2.get(\u0026#39;0.0\u0026#39;,\u0026#39;end\u0026#39;) != \u0026#34;\u0026#34;: txt2.delete(\u0026#39;0.0\u0026#39;,\u0026#39;end\u0026#39;) file = \u0026#39;grid.pdf\u0026#39; table = extract_table_from_pdf(file) #print(table) index = [1,2,3,4,5,6,7] df = pd.DataFrame(table[1:], columns=table[0], index=index) #print(df) #ip = sys.stdin.read().rstrip(\u0026#39;\\n\u0026#39;) ip_list = ip.splitlines() ip_list = [ip for ip in ip_list if ip.strip()] for i in range(len(ip_list)): ci = df.columns.get_loc(ip_list[i][0]) ri = df.index.get_loc(int(ip_list[i][1])) #print(df.iloc[ri,ci]) txt2.insert(END, df.iloc[ri,ci]) #txt2.insert(END, df.iloc[ri,ci]+\u0026#39;\\n\u0026#39;) except Exception as e: #print(f\u0026#34;An error occurred: {e}\u0026#34;) txt2.insert(END, f\u0026#34;An error occurred: {e}\u0026#34;) # 创建主窗口 root = tk.Tk() #root.iconbitmap(\u0026#34;favicon.ico\u0026#34;) #icon_path = \u0026#34;favicon.ico\u0026#34; # 设置窗口标题 root.title(\u0026#34;grid card转换器\u0026#34;) root.geometry(\u0026#39;460x300\u0026#39;) lb1 = Label(root, text=\u0026#39;请将密码表名称改为grid.pdf并与本程序放到同个目录下\u0026#39;) lb1.place(relx=0.1, rely=0.05) lb2 = Label(root, text=\u0026#39;复制二次验证网页的字符串到下方第一个窗口中并点击转换\\n对应密码会在第二个窗口出现\u0026#39;) lb2.place(relx=0.1, rely=0.15) txt1 = Text(root) txt1.place(relx=0.1, rely=0.3, relwidth=0.2, relheight=0.4) txt2 = Text(root) txt2.place(relx=0.6, rely=0.3, relwidth=0.2, relheight=0.4) btn1 = Button(root,text =\u0026#39;转换\u0026#39;,command=lambda:run1(txt1.get(\u0026#39;0.0\u0026#39;,\u0026#39;end\u0026#39;))) btn1.place(relx=0.4, rely=0.3, relwidth=0.1, relheight=0.1) myappid = \u0026#34;company.product.version\u0026#34; # 这里可以设置任意文本 ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid) root.wm_iconbitmap(r\u0026#39;D:\\person\\tansfer grid\\new version\\favicon.ico\u0026#39;) # 设置程序图标 # 运行主循环 root.mainloop() Golang 进行操作 如果想要搭建在线网址，可以用 golang 进行处理 优点: 直接复制表格，索引到网页，设置好参数即可识别返回结果 缺点: 需要上传数据到网站，增加了维护成本 展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 package trans import ( \u0026#34;encoding/csv\u0026#34; \u0026#34;fmt\u0026#34; \u0026#34;log\u0026#34; \u0026#34;os\u0026#34; \u0026#34;regexp\u0026#34; \u0026#34;strings\u0026#34; ) // 读取CSV文件并获取指定列和行的数据 func ReadCSV(filename string) [][]string { file, err := os.Open(filename) if err != nil { log.Fatal(err) } defer file.Close() reader := csv.NewReader(file) rows, err := reader.ReadAll() //fmt.Println(rows) if err != nil { log.Fatal(err) } //fmt.Println(rows) return rows } // 去除空格定义表格，进行取样 func Dealdata(data string, c_if int, r_if int, cc int, rr int) [][]string { if cc == 0 { cc = 12 fmt.Println(\u0026#34;cc等于12\u0026#34;) } if rr == 0 { rr = 7 fmt.Println(\u0026#34;rr等于7\u0026#34;) } if cc \u0026gt;= 20 || rr \u0026gt;= 20 { defer func() { if err := recover(); err != nil { fmt.Println(err) } }() l := make([][]string, 1) return l } l := make([][]string, rr) if c_if == 1 { //跳过前缀行 data = data[cc:] fmt.Println(len(data), data) } //通过note判断1个字符还是2个字符 var note int if len(data) \u0026gt; 91 { note = 2 } else { note = 1 } var sl string for i := 0; i \u0026lt; rr; i++ { //跳过行首序列 if r_if == 1 { if len(data) \u0026gt; cc*note { sl = data[1 : cc*note+1] } else if len(data) \u0026lt;= cc*note { sl = data[1:] } fmt.Println(sl) for n := 0; n+note \u0026lt; len(sl)+1; n = n + note { l[i] = append(l[i], sl[n:n+note]) } fmt.Println(l) if len(data) \u0026gt; cc*note { data = data[cc*note+1:] } } else { if len(data) \u0026gt; cc*note { sl = data[:cc*note] } else if len(data) \u0026lt;= cc*note { sl = data } //fmt.Println(sl) for n := 0; n+note \u0026lt; len(sl)+1; n = n + note { l[i] = append(l[i], sl[n:n+note]) } //fmt.Println(l) if len(data) \u0026gt; cc*note { data = data[cc*note:] } } } return l } func PreDeal(st string, q string) (string, []string) { //表格预处理 //只留下字符数字并将列表结果转为字符串 re := regexp.MustCompile(`\\w`) q = strings.Join(re.FindAllString(q, -1), \u0026#34;\u0026#34;) st = strings.Join(re.FindAllString(st, -1), \u0026#34;\u0026#34;) //fmt.Println(st, q) //查询转大写 q = strings.ToUpper(q) //按2个字符方式取表格 goal := splitStringByInterval(q, 2) return st, goal } func SearchData(st string, q string, c_if int, r_if int, cc int, rr int) string { //只留下字符数字并将列表结果转为字符串 var goal []string st, goal = PreDeal(st, q) fmt.Println(st) fmt.Println(c_if, r_if) //处理表格 data := Dealdata(st, c_if, r_if, cc, rr) //fmt.Println(data) ans := []string{} for _, idx := range goal { //列 m := string(idx[0]) ci := int([]rune(m)[0]) - int(\u0026#39;A\u0026#39;) //行 n := int(idx[1]) - int(\u0026#39;0\u0026#39;) ri := n - 1 //fmt.Println(ri, ci, string(data[ri][ci])) ans = append(ans, string(data[ri][ci])) } revise_ans := strings.Join(ans, \u0026#34;\u0026#34;) //fmt.Println(ans) return revise_ans } func splitStringByInterval(str string, interval int) []string { var data []string for i := 0; i \u0026lt; len(str); i += interval { end := i + interval if end \u0026gt; len(str) { end = len(str) } data = append(data, str[i:end]) } return data } ","date":"2024-09-19T00:00:00Z","permalink":"https://cancanneed64.github.io/p/%E8%A1%A8%E6%A0%BC%E7%B4%A2%E5%BC%95%E5%A4%9A%E7%A7%8D%E6%9F%A5%E8%AF%A2%E6%96%B9%E6%B3%95/","title":"表格索引多种查询方法"},{"content":"Python 爬取统计页面分析 需求: 实现 selenium 登录爬取页面数据，找到音频下载请求\n分析: 实现登录-保持请求状态-保存返回内容-找到请求列表-找到视频链接-下载 通过 selenium 登陆后返回 session，将其保存供发送请求使用，不需要重复登录，进而找到返回音频列表的 url，最后找到视频下载 url\n首先去到页面找到目标的 ID\n提交请求返回 segments-id 列表\n/search/#/search?searchStr=kayla%20chen\u0026amp;page=1\u0026amp;sort=1startTime22\u0026amp;range=1,1706693634219,1,1\n分析请求目标\n/search-service/v1/segments/query\n根据 segments-id 构造连接\nmedia-playback/v1/segments/\nd007e14b-186c-4d42-86bf-99be8635aae8?\nmedia-type=all\u0026amp;exclude-waveforms=false\u0026amp;exclude-qm-categories=false\n根据连接获取视频连接加上 token 返回下载\n/output_b15485ca-f140-4c19-b6ad-ab9813e386ac_.mp4?\rX-Amz-Security-Token=\rX-Amz-Algorithm=AWS4-HMAC-SHA256\u0026amp;\rX-Amz-Date=20240131T095335Z\u0026amp;\rX-Amz-SignedHeaders=host\u0026amp;\rX-Amz-Expires=2700\u0026amp;\rX-Amz-Credential=ASIA5L57TNZ4EBUW3QAZ%2F20240131%2Fus-west-2%2Fs3%2Faws4_request\u0026amp;\rX-Amz-Signature=c497fdefecb75d658377e34f2a1f6f7e190d54f6d3bf86d840e8768b92443d44\n下载视频需要向 amazonaws.com 请求生成一串 AWS4-HMAC-SHA256 处理过的 token，而时效大概为半小时\n通过 selenium 登陆后返回 session 首次登陆请求 session 展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 # coding:utf-8 from selenium import webdriver import requests import time import json import sys import yaml from selenium.webdriver.common.desired_capabilities import DesiredCapabilities from selenium.webdriver.remote.webdriver import WebDriver def read_service(): with open(\u0026#39;session.yaml\u0026#39;,encoding=\u0026#39;utf-8\u0026#39;) as f: session = yaml.safe_load(f) return session def main(): # 从命令行参数获取登录用户名和密码 user_name = \u0026#39;\u0026#39; password = \u0026#39;\u0026#39; # 登录页面URL login_url = \u0026#39;\u0026#39; # 获取chrome的配置 opt = webdriver.ChromeOptions() # 在运行的时候不弹出浏览器窗口 # opt.set_headless() opt.add_experimental_option(\u0026#39;detach\u0026#39;, True) # 获取driver对象 driver = webdriver.Chrome(opt) # 打开登录页面 driver.get(login_url) print(\u0026#39;opened login page...\u0026#39;) # 向浏览器发送用户名、密码，并点击登录按钮 time.sleep(2) # 多次登录需要输入验证码，这里给一个手工输入验证码的时间 print(\u0026#39;submited...\u0026#39;) # 等待2秒钟 time.sleep(20) #-------------- remote_executor = driver.command_executor._url session_id = driver.session_id print(remote_executor) print(session_id) service = dict([(remote_executor,session_id)]) with open(\u0026#39;session.yaml\u0026#39;,\u0026#39;w\u0026#39;,encoding=\u0026#39;utf-8\u0026#39;) as f: yaml.safe_dump(service,f) cookies = driver.get_cookies() print(cookies) #-------------- page_url = \u0026#34;/search-service/v1/segments/query\u0026#34; # 创建一个requests session对象 s = requests.Session() # 从driver中获取cookie列表（是一个列表，列表的每个元素都是一个字典） #cookies = driver.get_cookies() # 把cookies设置到session中 for cookie in cookies: s.cookies.set(cookie[\u0026#39;name\u0026#39;],cookie[\u0026#39;value\u0026#39;]) headers = { \u0026#34;User-Agent\u0026#34;:\u0026#39;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36\u0026#39;} resp = s.get(page_url,headers=headers) resp.encoding = \u0026#39;utf-8\u0026#39; print(\u0026#39;status_code = {0}\u0026#39;.format(resp.status_code)) print(resp.content.decode(\u0026#34;utf-8\u0026#34;)) if __name__ == \u0026#39;__main__\u0026#39;: main() 使用保存下来的 session 进行 request 请求 展开代码 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 # coding:utf-8 import requests import time import json import sys import yaml def main(): page_url = \u0026#34;/search-service/v1/segments/query\u0026#34; s = requests.Session() # 从driver中获取cookie列表（是一个列表，列表的每个元素都是一个字典） #cookies = driver.get_cookies() #print(cookies) # 把cookies设置到session中 cookies = [{}] for cookie in cookies: s.cookies.set(cookie[\u0026#39;name\u0026#39;],cookie[\u0026#39;value\u0026#39;]) headers ={ } resp = s.get(page_url,headers=headers) #resp = s.post(page_url) resp.encoding = \u0026#39;utf-8\u0026#39; print(\u0026#39;status_code = {0}\u0026#39;.format(resp.status_code)) print(resp.content.decode(\u0026#34;utf-8\u0026#34;)) if __name__ == \u0026#39;__main__\u0026#39;: main() 处理返回数据 这里为方便观察操作，将 网页中 Network 的 query 文件右键保存为 HAR 文件, 手动用 VSCODE 插件将 HAR 转为 JSON 文件，并读取文件提取 segmentId 和 acdContactId，再将这两个元素保存为 csv 格式进行读取 格式为477516744479，b37e8b9d-a040-4c82-b9e9-02b9904d3925 最后根据 acdContactId 可以找到视频下载链接\n请求下载视频 展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 import requests import time import json import csv import pandas as pd data = \u0026#39;\u0026#39;\u0026#39; b37e8b9d-a040-4c82-b9e9-02b9904d3925 7dc24219-6fb8-4adf-b510-3e346b7d6255 \u0026#39;\u0026#39;\u0026#39; data_list = data.split() def writeCsv(File,species): row = [File,species] out = open(\u0026#34;save3.csv\u0026#34;, \u0026#34;a\u0026#34;, newline=\u0026#34;\u0026#34;) csv_writer = csv.writer(out, dialect=\u0026#34;excel\u0026#34;) csv_writer.writerow(row) def read_csv(filename): ## with open(filename) as csvfile: ## reader = csv.DictReader(csvfile) ## print(reader) ## content = [print(a) for a in reader] ## column = [row[\u0026#34;b37e8b9d-a040-4c82-b9e9-02b9904d3925\u0026#34;] for row in reader] # weight 同列的数据 ## #print(column) data = pd.read_csv(filename,header=None) data.head() link_o = data.iloc[:,1] name = data.iloc[:,0] print(name,link_o) return name,link_o def download(url,name): with open(\u0026#39;{0}.mp4\u0026#39;.format(name), \u0026#34;wb\u0026#34;) as mp4: for chunk in url: if chunk: mp4.write(chunk) hd = { \u0026#39;Host\u0026#39;:\u0026#39;production-mcrplaybackmanager-fzgrfhdt0tfl.s3.us-west-2.amazonaws.com\u0026#39;, \u0026#39;Origin\u0026#39;:\u0026#39;\u0026#39;, \u0026#39;Referer\u0026#39;:\u0026#39;/\u0026#39;, \u0026#39;Sec-Ch-Ua\u0026#39;:\u0026#39;\u0026#34;Not A(Brand\u0026#34;;v=\u0026#34;99\u0026#34;, \u0026#34;Google Chrome\u0026#34;;v=\u0026#34;121\u0026#34;, \u0026#34;Chromium\u0026#34;;v=\u0026#34;121\u0026#34;\u0026#39;, \u0026#39;Sec-Fetch-Dest\u0026#39;:\u0026#39;video\u0026#39;, \u0026#39;Sec-Fetch-Mode\u0026#39;:\u0026#39;cors\u0026#39;, \u0026#39;Sec-Fetch-Site\u0026#39;:\u0026#39;cross-site\u0026#39;, \u0026#39;User-Agent\u0026#39;:\u0026#39;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36\u0026#39; } name,link_list = read_csv(\u0026#34;save3.csv\u0026#34;) for n,l in enumerate(link_list): try: resp = requests.get(l, headers=hd, stream=True) print(\u0026#39;status_code = {0}\u0026#39;.format(resp.status_code)) download(resp,name[n]) except Exception as e: print(e) ","date":"2024-02-07T00:00:00Z","permalink":"https://cancanneed64.github.io/p/python%E7%88%AC%E5%8F%96%E7%BB%9F%E8%AE%A1%E9%A1%B5%E9%9D%A2%E5%88%86%E6%9E%90/","title":"Python爬取统计页面分析"},{"content":"vba 统计座位关联计数 需求一: 检查所有单元格是否为合并单元格，拆分 流程\n1.合并单元格，循环无法遍历 解决方法，检测拆分 If Cells(i, k).MergeCells = True Then \u0026lsquo;Cells(i, k).UnMerge\n拆分单元格\n2.循环判断，跳过循环 Exit For\nElseIf Then\n其他参照 展开代码 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 Sub Cal() Application.ScreenUpdating = False Dim i, j As Integer Dim r As Range \u0026#39;统计数j j = 0 Set sh1 = Worksheets(\u0026#34;3F\u0026#34;) \u0026#39;行数i For i = 2 To 43 \u0026#39;列数k For k = 2 To 67 \u0026#39;Set r = Cells(i, 2).MergeArea \u0026#39;判断是否为合并单元格 If Cells(i, k).MergeCells = True Then Exit For \u0026#39;MsgBox (\u0026#34;1\u0026#34;) Else \u0026#39;如果正常为1则j计数 If Cells(i, k).Value = \u0026#34;1\u0026#34; Then j = j + 1 End If End If Next k Next i Cells(46, 2).Value = j Application.ScreenUpdating = Ture End Sub \u0026#39;j为1，h为2，g为3，f为4，d为5,s为9,z为0 需求二: 统计含特定字符的单元格数量 流程\n关键在于设置变量以及单元格范围 展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 VERSION 1.0 CLASS BEGIN MultiUse = -1 \u0026#39;True END Attribute VB_Name = \u0026#34;Sheet1\u0026#34; Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = False Attribute VB_PredeclaredId = True Attribute VB_Exposed = True Sub Cal() Application.ScreenUpdating = False Dim i, k, j, h, g, f, d, s, z As Integer \u0026#39;Dim r As Range \u0026#39;统计数j为1，h为2，g为3，f为4，d为5,s为9,z为0 i = 0 j = 0 k = 0 h = 0 g = 0 f = 0 d = 0 s = 0 z = 0 n = 53 Set sh1 = Worksheets(\u0026#34;2F\u0026#34;) \u0026#39;行数i For i = 2 To 48 \u0026#39;列数k For k = 2 To 28 \u0026#39;Set r = Cells(i, 2).MergeArea \u0026#39;判断是否为合并单元格 If Cells(i, k).MergeCells = True Then Cells(i, k).UnMerge \u0026#39;Exit For \u0026#39;MsgBox (\u0026#34;1\u0026#34;) End If \u0026#39;Else \u0026#39;如果正常，j为1，h为2，g为3，f为4，d为5,s为9,z为0 If Cells(i, k).Value = \u0026#34;0\u0026#34; Then z = z + 1 End If If Cells(i, k).Value = \u0026#34;1\u0026#34; Then j = j + 1 ElseIf Cells(i, k).Value = \u0026#34;1+2\u0026#34; Then j = j + 1 h = h + 1 ElseIf Cells(i, k).Value = \u0026#34;1+3\u0026#34; Then j = j + 1 g = g + 1 ElseIf Cells(i, k).Value = \u0026#34;1+4\u0026#34; Then j = j + 1 f = f + 1 ElseIf Cells(i, k).Value = \u0026#34;1+5\u0026#34; Then j = j + 1 s = s + 1 End If \u0026#39;j为1，h为2，g为3，f为4，d为5,s为9,z为0 If Cells(i, k).Value = \u0026#34;2\u0026#34; Then h = h + 1 ElseIf Cells(i, k).Value = \u0026#34;2+3\u0026#34; Then h = h + 1 g = g + 1 ElseIf Cells(i, k).Value = \u0026#34;2+4\u0026#34; Then h = h + 1 f = f + 1 ElseIf Cells(i, k).Value = \u0026#34;2+5\u0026#34; Then h = h + 1 d = d + 1 End If \u0026#39;j为1，h为2，g为3，f为4，d为5,s为9,z为0 If Cells(i, k).Value = \u0026#34;3\u0026#34; Then g = g + 1 ElseIf Cells(i, k).Value = \u0026#34;3+4\u0026#34; Then g = g + 1 f = f + 1 ElseIf Cells(i, k).Value = \u0026#34;3+5\u0026#34; Then g = g + 1 d = d + 1 End If If Cells(i, k).Value = \u0026#34;4\u0026#34; Then f = f + 1 ElseIf Cells(i, k).Value = \u0026#34;4+5\u0026#34; Then f = f + 1 d = d + 1 End If If Cells(i, k).Value = \u0026#34;5\u0026#34; Then d = d + 1 End If If Cells(i, k).Value = \u0026#34;9\u0026#34; Then s = s + 1 ElseIf Cells(i, k).Value = \u0026#34;9+2\u0026#34; Then s = s + 1 h = h + 9 ElseIf Cells(i, k).Value = \u0026#34;9+3\u0026#34; Then s = s + 1 g = g + 1 ElseIf Cells(i, k).Value = \u0026#34;9+4\u0026#34; Then s = s + 1 f = f + 1 ElseIf Cells(i, k).Value = \u0026#34;9+5\u0026#34; Then s = s + 1 d = d + 1 End If Next k Next i Cells(n, 4).Value = z Cells(n + 1, 4).Value = j Cells(n + 2, 4).Value = h Cells(n + 3, 4).Value = g Cells(n + 4, 4).Value = f Cells(n + 5, 4).Value = d Cells(n + 6, 4).Value = s Application.ScreenUpdating = Ture End Sub 2F 改进 展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 Sub Cal() Application.ScreenUpdating = False Dim i, k, j, h, g, f, d, s, z, a, b As Integer \u0026#39;Dim a1 As Integer \u0026#39;Dim r As Range \u0026#39;统计数j为1，h为2，g为3，f为4，d为6,s为9,z为0,a8,b7 i = 0 \u0026#39;row k = 0 \u0026#39;col n = 53 \u0026#39;write the number j = 0 \u0026#39;TIO24 gen3 1 h = 0 \u0026#39;left 2 g = 0 \u0026#39;mid up 3 f = 0 \u0026#39;mid down 4 d = 0 \u0026#39;wfh mon 6 s = 0 \u0026#39;another tio24 9 z = 0 \u0026#39;HP 0 a = 0 \u0026#39;dual mon 8 b = 0 \u0026#39;TIO24 gen5 7 Set sh1 = Worksheets(\u0026#34;2F\u0026#34;) \u0026#39;行数i For i = 2 To 49 \u0026#39;TODO \u0026#39;列数k For k = 2 To 29 \u0026#39;TODO \u0026#39;Set r = Cells(i, 2).MergeArea \u0026#39;判断是否为合并单元格 If Cells(i, k).MergeCells = True Then Cells(i, k).UnMerge \u0026#39;Exit For \u0026#39;MsgBox (\u0026#34;1\u0026#34;) End If \u0026#39;Else \u0026#39;如果正常，j为1，h为2，g为3，f为4，d为6,s为9,z为0,a8,b7 If Cells(i, k).Value = \u0026#34;0\u0026#34; Then z = z + 1 ElseIf Cells(i, k).Value = \u0026#34;0+8\u0026#34; Then z = z + 1 a = a + 1 ElseIf Cells(i, k).Value = \u0026#34;0+6\u0026#34; Then z = z + 1 d = d + 1 ElseIf Cells(i, k).Value = \u0026#34;0+7\u0026#34; Then z = z + 1 b = b + 1 ElseIf Cells(i, k).Value = \u0026#34;0+9\u0026#34; Then z = z + 1 s = s + 1 ElseIf Cells(i, k).Value = \u0026#34;0+1\u0026#34; Then z = z + 1 j = j + 1 ElseIf Cells(i, k).Value = \u0026#34;0+2\u0026#34; Then z = z + 1 h = h + 1 ElseIf Cells(i, k).Value = \u0026#34;0+3\u0026#34; Then z = z + 1 g = g + 1 ElseIf Cells(i, k).Value = \u0026#34;0+4\u0026#34; Then z = z + 1 f = f + 1 End If If Cells(i, k).Value = \u0026#34;1\u0026#34; Then j = j + 1 ElseIf Cells(i, k).Value = \u0026#34;1+8\u0026#34; Then j = j + 1 a = a + 1 ElseIf Cells(i, k).Value = \u0026#34;1+6\u0026#34; Then j = j + 1 d = d + 1 ElseIf Cells(i, k).Value = \u0026#34;1+7\u0026#34; Then j = j + 1 b = b + 1 ElseIf Cells(i, k).Value = \u0026#34;1+9\u0026#34; Then j = j + 1 s = s + 1 ElseIf Cells(i, k).Value = \u0026#34;1+0\u0026#34; Then j = j + 1 z = z + 1 ElseIf Cells(i, k).Value = \u0026#34;1+2\u0026#34; Then j = j + 1 h = h + 1 ElseIf Cells(i, k).Value = \u0026#34;1+3\u0026#34; Then j = j + 1 g = g + 1 ElseIf Cells(i, k).Value = \u0026#34;1+4\u0026#34; Then j = j + 1 f = f + 1 End If If Cells(i, k).Value = \u0026#34;2\u0026#34; Then h = h + 1 ElseIf Cells(i, k).Value = \u0026#34;2+8\u0026#34; Then h = h + 1 a = a + 1 ElseIf Cells(i, k).Value = \u0026#34;2+6\u0026#34; Then h = h + 1 d = d + 1 ElseIf Cells(i, k).Value = \u0026#34;2+7\u0026#34; Then h = h + 1 b = b + 1 ElseIf Cells(i, k).Value = \u0026#34;2+9\u0026#34; Then h = h + 1 s = s + 1 ElseIf Cells(i, k).Value = \u0026#34;2+0\u0026#34; Then h = h + 1 z = z + 1 ElseIf Cells(i, k).Value = \u0026#34;2+1\u0026#34; Then h = h + 1 j = j + 1 ElseIf Cells(i, k).Value = \u0026#34;2+3\u0026#34; Then h = h + 1 g = g + 1 ElseIf Cells(i, k).Value = \u0026#34;2+4\u0026#34; Then h = h + 1 f = f + 1 End If If Cells(i, k).Value = \u0026#34;3\u0026#34; Then g = g + 1 ElseIf Cells(i, k).Value = \u0026#34;3+8\u0026#34; Then g = g + 1 a = a + 1 ElseIf Cells(i, k).Value = \u0026#34;3+6\u0026#34; Then g = g + 1 d = d + 1 ElseIf Cells(i, k).Value = \u0026#34;3+7\u0026#34; Then g = g + 1 b = b + 1 ElseIf Cells(i, k).Value = \u0026#34;3+9\u0026#34; Then g = g + 1 s = s + 1 ElseIf Cells(i, k).Value = \u0026#34;3+0\u0026#34; Then g = g + 1 z = z + 1 ElseIf Cells(i, k).Value = \u0026#34;3+1\u0026#34; Then g = g + 1 j = j + 1 ElseIf Cells(i, k).Value = \u0026#34;3+2\u0026#34; Then g = g + 1 h = h + 1 ElseIf Cells(i, k).Value = \u0026#34;3+4\u0026#34; Then g = g + 1 f = f + 1 End If If Cells(i, k).Value = \u0026#34;4\u0026#34; Then f = f + 1 ElseIf Cells(i, k).Value = \u0026#34;4+8\u0026#34; Then f = f + 1 a = a + 1 ElseIf Cells(i, k).Value = \u0026#34;4+6\u0026#34; Then f = f + 1 d = d + 1 ElseIf Cells(i, k).Value = \u0026#34;4+7\u0026#34; Then f = f + 1 b = b + 1 ElseIf Cells(i, k).Value = \u0026#34;4+9\u0026#34; Then f = f + 1 s = s + 1 ElseIf Cells(i, k).Value = \u0026#34;4+0\u0026#34; Then f = f + 1 z = z + 1 ElseIf Cells(i, k).Value = \u0026#34;4+1\u0026#34; Then f = f + 1 j = j + 1 ElseIf Cells(i, k).Value = \u0026#34;4+2\u0026#34; Then f = f + 1 h = h + 1 ElseIf Cells(i, k).Value = \u0026#34;4+3\u0026#34; Then f = f + 1 g = g + 1 End If If Cells(i, k).Value = \u0026#34;6\u0026#34; Then d = d + 1 ElseIf Cells(i, k).Value = \u0026#34;6+8\u0026#34; Then d = d + 1 a = a + 1 ElseIf Cells(i, k).Value = \u0026#34;6+4\u0026#34; Then d = d + 1 f = f + 1 ElseIf Cells(i, k).Value = \u0026#34;6+7\u0026#34; Then d = d + 1 b = b + 1 ElseIf Cells(i, k).Value = \u0026#34;6+9\u0026#34; Then d = d + 1 s = s + 1 ElseIf Cells(i, k).Value = \u0026#34;6+0\u0026#34; Then d = d + 1 z = z + 1 ElseIf Cells(i, k).Value = \u0026#34;6+1\u0026#34; Then d = d + 1 j = j + 1 ElseIf Cells(i, k).Value = \u0026#34;6+2\u0026#34; Then d = d + 1 h = h + 1 ElseIf Cells(i, k).Value = \u0026#34;6+3\u0026#34; Then d = d + 1 g = g + 1 End If If Cells(i, k).Value = \u0026#34;9\u0026#34; Then s = s + 1 ElseIf Cells(i, k).Value = \u0026#34;9+8\u0026#34; Then s = s + 1 a = a + 1 ElseIf Cells(i, k).Value = \u0026#34;9+4\u0026#34; Then s = s + 1 f = f + 1 ElseIf Cells(i, k).Value = \u0026#34;9+7\u0026#34; Then s = s + 1 b = b + 1 ElseIf Cells(i, k).Value = \u0026#34;9+6\u0026#34; Then s = s + 1 d = d + 1 ElseIf Cells(i, k).Value = \u0026#34;9+0\u0026#34; Then s = s + 1 z = z + 1 ElseIf Cells(i, k).Value = \u0026#34;9+1\u0026#34; Then s = s + 1 j = j + 1 ElseIf Cells(i, k).Value = \u0026#34;9+2\u0026#34; Then s = s + 1 h = h + 1 ElseIf Cells(i, k).Value = \u0026#34;9+3\u0026#34; Then s = s + 1 g = g + 1 End If If Cells(i, k).Value = \u0026#34;8\u0026#34; Then a = a + 1 ElseIf Cells(i, k).Value = \u0026#34;8+0\u0026#34; Then a = a + 1 z = z + 1 ElseIf Cells(i, k).Value = \u0026#34;8+4\u0026#34; Then a = a + 1 f = f + 1 ElseIf Cells(i, k).Value = \u0026#34;8+7\u0026#34; Then a = a + 1 b = b + 1 ElseIf Cells(i, k).Value = \u0026#34;8+6\u0026#34; Then a = a + 1 d = d + 1 ElseIf Cells(i, k).Value = \u0026#34;8+9\u0026#34; Then a = a + 1 s = s + 1 ElseIf Cells(i, k).Value = \u0026#34;8+1\u0026#34; Then a = a + 1 j = j + 1 ElseIf Cells(i, k).Value = \u0026#34;8+2\u0026#34; Then a = a + 1 h = h + 1 ElseIf Cells(i, k).Value = \u0026#34;8+3\u0026#34; Then a = a + 1 g = g + 1 End If If Cells(i, k).Value = \u0026#34;7\u0026#34; Then b = b + 1 ElseIf Cells(i, k).Value = \u0026#34;7+0\u0026#34; Then b = b + 1 z = z + 1 ElseIf Cells(i, k).Value = \u0026#34;7+4\u0026#34; Then b = b + 1 f = f + 1 ElseIf Cells(i, k).Value = \u0026#34;7+8\u0026#34; Then b = b + 1 a = a + 1 ElseIf Cells(i, k).Value = \u0026#34;7+6\u0026#34; Then b = b + 1 d = d + 1 ElseIf Cells(i, k).Value = \u0026#34;7+9\u0026#34; Then b = b + 1 s = s + 1 ElseIf Cells(i, k).Value = \u0026#34;7+1\u0026#34; Then b = b + 1 j = j + 1 ElseIf Cells(i, k).Value = \u0026#34;7+2\u0026#34; Then b = b + 1 h = h + 1 ElseIf Cells(i, k).Value = \u0026#34;7+3\u0026#34; Then b = b + 1 g = g + 1 End If Next k Next i Cells(n, 4).Value = z \u0026#39;0 Cells(n + 1, 4).Value = j \u0026#39;1 Cells(n + 2, 4).Value = h \u0026#39;2 Cells(n + 3, 4).Value = g \u0026#39;3 Cells(n + 4, 4).Value = f \u0026#39;4 Cells(n + 5, 4).Value = d \u0026#39;6 Cells(n + 6, 4).Value = s \u0026#39;9 Cells(n + 7, 4).Value = a \u0026#39;8 Cells(n + 8, 4).Value = b \u0026#39;7 Application.ScreenUpdating = Ture End Sub 3F 改进 展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 Sub Cal() Application.ScreenUpdating = False Dim i, k, j, h, g, f, d, s, z, a, b As Integer \u0026#39;Dim a1 As Integer \u0026#39;Dim r As Range \u0026#39;统计数j为1，h为2，g为3，f为4，d为6,s为9,z为0,a8,b7 i = 0 \u0026#39;row k = 0 \u0026#39;col n = 53 \u0026#39;write the number j = 0 \u0026#39;TIO24 gen3 1 h = 0 \u0026#39;left 2 g = 0 \u0026#39;mid up 3 f = 0 \u0026#39;mid down 4 d = 0 \u0026#39;wfh mon 6 s = 0 \u0026#39;another tio24 9 z = 0 \u0026#39;HP 0 a = 0 \u0026#39;dual mon 8 b = 0 \u0026#39;TIO24 gen5 7 Set sh1 = Worksheets(\u0026#34;3F\u0026#34;) \u0026#39;行数i For i = 2 To 43 \u0026#39;TODO \u0026#39;列数k For k = 2 To 68 \u0026#39;TODO \u0026#39;Set r = Cells(i, 2).MergeArea \u0026#39;判断是否为合并单元格 If Cells(i, k).MergeCells = True Then Cells(i, k).UnMerge \u0026#39;Exit For \u0026#39;MsgBox (\u0026#34;1\u0026#34;) End If \u0026#39;Else \u0026#39;如果正常，j为1，h为2，g为3，f为4，d为6,s为9,z为0,a8,b7 If Cells(i, k).Value = \u0026#34;0\u0026#34; Then z = z + 1 ElseIf Cells(i, k).Value = \u0026#34;0+8\u0026#34; Then z = z + 1 a = a + 1 ElseIf Cells(i, k).Value = \u0026#34;0+6\u0026#34; Then z = z + 1 d = d + 1 ElseIf Cells(i, k).Value = \u0026#34;0+7\u0026#34; Then z = z + 1 b = b + 1 ElseIf Cells(i, k).Value = \u0026#34;0+9\u0026#34; Then z = z + 1 s = s + 1 ElseIf Cells(i, k).Value = \u0026#34;0+1\u0026#34; Then z = z + 1 j = j + 1 ElseIf Cells(i, k).Value = \u0026#34;0+2\u0026#34; Then z = z + 1 h = h + 1 ElseIf Cells(i, k).Value = \u0026#34;0+3\u0026#34; Then z = z + 1 g = g + 1 ElseIf Cells(i, k).Value = \u0026#34;0+4\u0026#34; Then z = z + 1 f = f + 1 End If If Cells(i, k).Value = \u0026#34;1\u0026#34; Then j = j + 1 ElseIf Cells(i, k).Value = \u0026#34;1+8\u0026#34; Then j = j + 1 a = a + 1 ElseIf Cells(i, k).Value = \u0026#34;1+6\u0026#34; Then j = j + 1 d = d + 1 ElseIf Cells(i, k).Value = \u0026#34;1+7\u0026#34; Then j = j + 1 b = b + 1 ElseIf Cells(i, k).Value = \u0026#34;1+9\u0026#34; Then j = j + 1 s = s + 1 ElseIf Cells(i, k).Value = \u0026#34;1+0\u0026#34; Then j = j + 1 z = z + 1 ElseIf Cells(i, k).Value = \u0026#34;1+2\u0026#34; Then j = j + 1 h = h + 1 ElseIf Cells(i, k).Value = \u0026#34;1+3\u0026#34; Then j = j + 1 g = g + 1 ElseIf Cells(i, k).Value = \u0026#34;1+4\u0026#34; Then j = j + 1 f = f + 1 End If If Cells(i, k).Value = \u0026#34;2\u0026#34; Then h = h + 1 ElseIf Cells(i, k).Value = \u0026#34;2+8\u0026#34; Then h = h + 1 a = a + 1 ElseIf Cells(i, k).Value = \u0026#34;2+6\u0026#34; Then h = h + 1 d = d + 1 ElseIf Cells(i, k).Value = \u0026#34;2+7\u0026#34; Then h = h + 1 b = b + 1 ElseIf Cells(i, k).Value = \u0026#34;2+9\u0026#34; Then h = h + 1 s = s + 1 ElseIf Cells(i, k).Value = \u0026#34;2+0\u0026#34; Then h = h + 1 z = z + 1 ElseIf Cells(i, k).Value = \u0026#34;2+1\u0026#34; Then h = h + 1 j = j + 1 ElseIf Cells(i, k).Value = \u0026#34;2+3\u0026#34; Then h = h + 1 g = g + 1 ElseIf Cells(i, k).Value = \u0026#34;2+4\u0026#34; Then h = h + 1 f = f + 1 End If If Cells(i, k).Value = \u0026#34;3\u0026#34; Then g = g + 1 ElseIf Cells(i, k).Value = \u0026#34;3+8\u0026#34; Then g = g + 1 a = a + 1 ElseIf Cells(i, k).Value = \u0026#34;3+6\u0026#34; Then g = g + 1 d = d + 1 ElseIf Cells(i, k).Value = \u0026#34;3+7\u0026#34; Then g = g + 1 b = b + 1 ElseIf Cells(i, k).Value = \u0026#34;3+9\u0026#34; Then g = g + 1 s = s + 1 ElseIf Cells(i, k).Value = \u0026#34;3+0\u0026#34; Then g = g + 1 z = z + 1 ElseIf Cells(i, k).Value = \u0026#34;3+1\u0026#34; Then g = g + 1 j = j + 1 ElseIf Cells(i, k).Value = \u0026#34;3+2\u0026#34; Then g = g + 1 h = h + 1 ElseIf Cells(i, k).Value = \u0026#34;3+4\u0026#34; Then g = g + 1 f = f + 1 End If If Cells(i, k).Value = \u0026#34;4\u0026#34; Then f = f + 1 ElseIf Cells(i, k).Value = \u0026#34;4+8\u0026#34; Then f = f + 1 a = a + 1 ElseIf Cells(i, k).Value = \u0026#34;4+6\u0026#34; Then f = f + 1 d = d + 1 ElseIf Cells(i, k).Value = \u0026#34;4+7\u0026#34; Then f = f + 1 b = b + 1 ElseIf Cells(i, k).Value = \u0026#34;4+9\u0026#34; Then f = f + 1 s = s + 1 ElseIf Cells(i, k).Value = \u0026#34;4+0\u0026#34; Then f = f + 1 z = z + 1 ElseIf Cells(i, k).Value = \u0026#34;4+1\u0026#34; Then f = f + 1 j = j + 1 ElseIf Cells(i, k).Value = \u0026#34;4+2\u0026#34; Then f = f + 1 h = h + 1 ElseIf Cells(i, k).Value = \u0026#34;4+3\u0026#34; Then f = f + 1 g = g + 1 End If If Cells(i, k).Value = \u0026#34;6\u0026#34; Then d = d + 1 ElseIf Cells(i, k).Value = \u0026#34;6+8\u0026#34; Then d = d + 1 a = a + 1 ElseIf Cells(i, k).Value = \u0026#34;6+4\u0026#34; Then d = d + 1 f = f + 1 ElseIf Cells(i, k).Value = \u0026#34;6+7\u0026#34; Then d = d + 1 b = b + 1 ElseIf Cells(i, k).Value = \u0026#34;6+9\u0026#34; Then d = d + 1 s = s + 1 ElseIf Cells(i, k).Value = \u0026#34;6+0\u0026#34; Then d = d + 1 z = z + 1 ElseIf Cells(i, k).Value = \u0026#34;6+1\u0026#34; Then d = d + 1 j = j + 1 ElseIf Cells(i, k).Value = \u0026#34;6+2\u0026#34; Then d = d + 1 h = h + 1 ElseIf Cells(i, k).Value = \u0026#34;6+3\u0026#34; Then d = d + 1 g = g + 1 End If If Cells(i, k).Value = \u0026#34;9\u0026#34; Then s = s + 1 ElseIf Cells(i, k).Value = \u0026#34;9+8\u0026#34; Then s = s + 1 a = a + 1 ElseIf Cells(i, k).Value = \u0026#34;9+4\u0026#34; Then s = s + 1 f = f + 1 ElseIf Cells(i, k).Value = \u0026#34;9+7\u0026#34; Then s = s + 1 b = b + 1 ElseIf Cells(i, k).Value = \u0026#34;9+6\u0026#34; Then s = s + 1 d = d + 1 ElseIf Cells(i, k).Value = \u0026#34;9+0\u0026#34; Then s = s + 1 z = z + 1 ElseIf Cells(i, k).Value = \u0026#34;9+1\u0026#34; Then s = s + 1 j = j + 1 ElseIf Cells(i, k).Value = \u0026#34;9+2\u0026#34; Then s = s + 1 h = h + 1 ElseIf Cells(i, k).Value = \u0026#34;9+3\u0026#34; Then s = s + 1 g = g + 1 End If If Cells(i, k).Value = \u0026#34;8\u0026#34; Then a = a + 1 ElseIf Cells(i, k).Value = \u0026#34;8+0\u0026#34; Then a = a + 1 z = z + 1 ElseIf Cells(i, k).Value = \u0026#34;8+4\u0026#34; Then a = a + 1 f = f + 1 ElseIf Cells(i, k).Value = \u0026#34;8+7\u0026#34; Then a = a + 1 b = b + 1 ElseIf Cells(i, k).Value = \u0026#34;8+6\u0026#34; Then a = a + 1 d = d + 1 ElseIf Cells(i, k).Value = \u0026#34;8+9\u0026#34; Then a = a + 1 s = s + 1 ElseIf Cells(i, k).Value = \u0026#34;8+1\u0026#34; Then a = a + 1 j = j + 1 ElseIf Cells(i, k).Value = \u0026#34;8+2\u0026#34; Then a = a + 1 h = h + 1 ElseIf Cells(i, k).Value = \u0026#34;8+3\u0026#34; Then a = a + 1 g = g + 1 End If If Cells(i, k).Value = \u0026#34;7\u0026#34; Then b = b + 1 ElseIf Cells(i, k).Value = \u0026#34;7+0\u0026#34; Then b = b + 1 z = z + 1 ElseIf Cells(i, k).Value = \u0026#34;7+4\u0026#34; Then b = b + 1 f = f + 1 ElseIf Cells(i, k).Value = \u0026#34;7+8\u0026#34; Then b = b + 1 a = a + 1 ElseIf Cells(i, k).Value = \u0026#34;7+6\u0026#34; Then b = b + 1 d = d + 1 ElseIf Cells(i, k).Value = \u0026#34;7+9\u0026#34; Then b = b + 1 s = s + 1 ElseIf Cells(i, k).Value = \u0026#34;7+1\u0026#34; Then b = b + 1 j = j + 1 ElseIf Cells(i, k).Value = \u0026#34;7+2\u0026#34; Then b = b + 1 h = h + 1 ElseIf Cells(i, k).Value = \u0026#34;7+3\u0026#34; Then b = b + 1 g = g + 1 End If Next k Next i Cells(n, 4).Value = z \u0026#39;0 Cells(n + 1, 4).Value = j \u0026#39;1 Cells(n + 2, 4).Value = h \u0026#39;2 Cells(n + 3, 4).Value = g \u0026#39;3 Cells(n + 4, 4).Value = f \u0026#39;4 Cells(n + 5, 4).Value = d \u0026#39;6 Cells(n + 6, 4).Value = s \u0026#39;9 Cells(n + 7, 4).Value = a \u0026#39;8 Cells(n + 8, 4).Value = b \u0026#39;7 Application.ScreenUpdating = Ture End Sub 需求三: 两层映射对应关系 流程\n根据交换机导出的 mac 地址与端口号，主机名汇总出一份表格，名主机表 Ports Hostname Switch Gi1/0/44 P5R0E CANRSCS01\n清洗已有端口号与座位端口号的表格，汇总出一份表格，名网口表 Seat Ports 1-01 Gi1/0/3\n将地图的座位号标志好，名 map 表\n将主机表的端口和主机名创建字典 1，网口表的座位号和端口创建字典 2，遍历循环判断 map 表单元格是否有座位，可得到对应的端口号和主机名，改写 map 表单元格 展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 Sub data_match() Application.ScreenUpdating = False Dim dic As Object Dim arr Dim i As Integer Dim k As String Set st2 = Worksheets(\u0026#34;sum1\u0026#34;) \u0026#39;g to pc Set st22 = Worksheets(\u0026#34;sum2\u0026#34;) \u0026#39;g to pc Set st1 = Worksheets(\u0026#34;map\u0026#34;) \u0026#39;t Set st3 = Worksheets(\u0026#34;s1\u0026#34;) \u0026#39;d to g Set st4 = Worksheets(\u0026#34;s2\u0026#34;) \u0026#39;d to g Set dic = CreateObject(\u0026#34;scripting.dictionary\u0026#34;) \u0026#39; g to pc 1 Set dic1 = CreateObject(\u0026#34;scripting.dictionary\u0026#34;) \u0026#39; g to pc 1 Set dic2 = CreateObject(\u0026#34;scripting.dictionary\u0026#34;) \u0026#39; d to g 5 Set dic3 = CreateObject(\u0026#34;scripting.dictionary\u0026#34;) \u0026#39; d to g 6 arr = st2.Cells(1, 1).CurrentRegion \u0026#39;总表 For i = 1 To UBound(arr, 1) \u0026#39;这里字典的值，用的是array数组，方便我们一下匹配多个数据，省去再创建字典对象麻烦。 dic(arr(i, 1)) = arr(i, 2) \u0026#39; Gi1/0/44 P5R0E CANRSCS01 Next arr1 = st22.Cells(1, 1).CurrentRegion \u0026#39;总表 For i = 1 To UBound(arr1, 1) \u0026#39;这里字典的值，用的是array数组，方便我们一下匹配多个数据，省去再创建字典对象麻烦。 dic1(arr1(i, 1)) = arr1(i, 2) \u0026#39; Gi1/0/44 P5R0E CANRSCS01 Next \u0026#39;Debug.Print dic(\u0026#34;Gi1/0/44\u0026#34;) arr2 = st3.Cells(1, 1).CurrentRegion \u0026#39;s1 For i = 1 To UBound(arr2, 1) - 1 \u0026#39;这里字典的值，用的是array数组，方便我们一下匹配多个数据，省去再创建字典对象麻烦。 dic2(arr2(i, 1)) = arr2(i, 2) \u0026#39;551 Gi1/0/44 \u0026#39;Debug.Print TypeName(arr2(i, 1)), TypeName(arr2(i, 2)) Next arr3 = st4.Cells(1, 1).CurrentRegion \u0026#39;s2 For i = 1 To UBound(arr3, 1) - 1 \u0026#39;这里字典的值，用的是array数组，方便我们一下匹配多个数据，省去再创建字典对象麻烦。 dic3(arr3(i, 1)) = arr3(i, 2) \u0026#39;dic3(Join(Array(arr3(i, 1), arr3(i, 3)), \u0026#34;|\u0026#34;)) = arr3(i, 2) \u0026#39;551 Gi1/0/44 \u0026#39; D|CS = PORT \u0026#39; \u0026#34;1-01|CANRSCS02\u0026#34; : Variant/String Next \u0026#39;Debug.Print dic3(\u0026#34;1-01|CANRSCS02\u0026#34;) \u0026#39;For Each Key In dic3.items \u0026#39;Debug.Print Key \u0026#39;Next \u0026#39;Debug.Print dic3.Key(\u0026#34;Gi1/0/5\u0026#34;) \u0026#39;Debug.Print dic2(551) \u0026#39;k = dic(\u0026#34;Gi1/0/44\u0026#34;) \u0026#39;Debug.Print k For i = 19 To 19 \u0026#39;2 To 43 For j = 63 To 63 \u0026#39;2 To 68 Debug.Print st1.Cells(i, j).Value, TypeName(st1.Cells(i, j).Value) Debug.Print IsNumeric(st1.Cells(i, j).Value) Debug.Print Application.IsNumber(st1.Cells(i, j)) If Application.IsNumber(st1.Cells(i, j)) Then \u0026#39;是否为数字与字符串 \u0026#39;Debug.Print TypeName(st1.Cells(i, j).Value) \u0026#39;Debug.Print st1.Cells(i, j).Value \u0026#39;Debug.Print TypeName(dic2(st1.Cells(i, j).Value)) \u0026#39;Debug.Print dic(dic2(st1.Cells(i, j).Value)) \u0026#39;Debug.Print TypeName(dic(dic2(Int(st1.Cells(i, j).Value)))) \u0026#39;Debug.Print dic(dic2(Int(st1.Cells(i, j).Value))) v = st1.Cells(i, j).Value Debug.Print v \u0026#39;map的D值 - s1的g和s - sum的sn If dic2.exists(v) Then a = dic2(v) b = dic.exists(a) \u0026#39;元素是否存在 If b Then \u0026#39;Debug.Print \u0026#34;1\u0026#34; st1.Cells(i, j).Value = dic(a) \u0026#39;赋值 \u0026#39;st1.Cells(i, j).Value = dic(dic2(Int(st1.Cells(i, j).Value))) End If End If If dic3.exists(v) Then a = dic3(v) b = dic1.exists(a) If b Then st1.Cells(i, j).Value = dic1(a) \u0026#39;赋值 \u0026#39;Debug.Print st1.Cells(i, j).Value \u0026#39;st1.Cells(i, j).Value = dic(dic2(Int(st1.Cells(i, j).Value))) End If \u0026#39; Debug.Print TypeName(b), b \u0026#39;Debug.Print dic(dic2(Int(st1.Cells(i, j).Value))) \u0026#39;dic2(int) dic(str) End If End If Next Next Set dic = Nothing Set dic2 = Nothing Application.ScreenUpdating = True End Sub ","date":"2023-07-12T00:00:00Z","permalink":"https://cancanneed64.github.io/p/vba%E7%BB%9F%E8%AE%A1%E5%BA%A7%E4%BD%8D%E5%85%B3%E8%81%94%E8%AE%A1%E6%95%B0/","title":"vba统计座位关联计数"},{"content":"geektutu gee 框架 HTTP 基础 核心\n1 2 3 http.HandleFunc(\u0026#34;/\u0026#34;, indexHandler) http.HandleFunc(\u0026#34;/hello\u0026#34;, helloHandler) log.Fatal(http.ListenAndServe(\u0026#34;:9999\u0026#34;, nil)) 通过捆绑路径和 url 类型为 w http.ResponseWriter, req *http.Request\n实现方法 ServeHTTP\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 type Engine struct{} func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { switch req.URL.Path { case \u0026#34;/\u0026#34;: fmt.Fprintf(w, \u0026#34;URL.Path = %q\\n\u0026#34;, req.URL.Path) case \u0026#34;/hello\u0026#34;: for k, v := range req.Header { fmt.Fprintf(w, \u0026#34;Header[%q] = %q\\n\u0026#34;, k, v) } default: fmt.Fprintf(w, \u0026#34;404 NOT FOUND: %s\\n\u0026#34;, req.URL) } } func main() { engine := new(Engine) log.Fatal(http.ListenAndServe(\u0026#34;:9999\u0026#34;, engine)) } 通过定义 Engine 结构体实现 ServeHTTP 再传入 http.ListenAndServe 第二个参数，ServeHTTP 封装了两个路径\n封装 gee 包\n1 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 32 33 34 35 36 37 38 39 40 41 // HandlerFunc defines the request handler used by gee type HandlerFunc func(http.ResponseWriter, *http.Request) // Engine implement the interface of ServeHTTP type Engine struct { router map[string]HandlerFunc } // New is the constructor of gee.Engine func New() *Engine { return \u0026amp;Engine{router: make(map[string]HandlerFunc)} } func (engine *Engine) addRoute(method string, pattern string, handler HandlerFunc) { key := method + \u0026#34;-\u0026#34; + pattern engine.router[key] = handler } // GET defines the method to add GET request func (engine *Engine) GET(pattern string, handler HandlerFunc) { engine.addRoute(\u0026#34;GET\u0026#34;, pattern, handler) } // POST defines the method to add POST request func (engine *Engine) POST(pattern string, handler HandlerFunc) { engine.addRoute(\u0026#34;POST\u0026#34;, pattern, handler) } // Run defines the method to start a http server func (engine *Engine) Run(addr string) (err error) { return http.ListenAndServe(addr, engine) } func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { key := req.Method + \u0026#34;-\u0026#34; + req.URL.Path if handler, ok := engine.router[key]; ok { handler(w, req) } else { fmt.Fprintf(w, \u0026#34;404 NOT FOUND: %s\\n\u0026#34;, req.URL) } } 定义结构体时创建一个 router 的 map 将原先 func(w http.ResponseWriter, req *http.Request)放入 HandlerFunc 中 addRoute 将 method + \u0026ldquo;-\u0026rdquo; + pattern 构造成 router 的 key，value 为 handler 方法只需要传入 engine.addRoute(\u0026ldquo;GET\u0026rdquo;, pattern, handler) 通过 Run 来监听 http.ListenAndServe(addr, engine) ServeHTTP 构造 key := req.Method + \u0026ldquo;-\u0026rdquo; + req.URL.Path，判断是否在 router 中\n上下文 context\n1 2 3 4 r := gee.New() r.GET(\u0026#34;/\u0026#34;, func(c *gee.Context) { c.HTML(http.StatusOK, \u0026#34;\u0026lt;h1\u0026gt;Hello Gee\u0026lt;/h1\u0026gt;\u0026#34;) }) 将 context 作为匿名函数放进 Handler\nmap[string]interface{}起了一个别名 gee.H，构建 JSON 数据时,可以直接封装\n1 2 3 4 c.JSON(http.StatusOK, gee.H{ \u0026#34;username\u0026#34;: c.PostForm(\u0026#34;username\u0026#34;), \u0026#34;password\u0026#34;: c.PostForm(\u0026#34;password\u0026#34;), }) router 将 addrouter 拿出修改为支持动态路由，handle 参数变为 context\ngee ServeHTTP 通过 router 的 handle 接管 context\n前缀树路由 定义节点\n1 2 3 4 5 6 type node struct { pattern string // 待匹配路由，例如 /p/:lang part string // 路由中的一部分，例如 :lang children []*node // 子节点，例如 [doc, tutorial, intro] isWild bool // 是否精确匹配，part 含有 : 或 * 时为true } trie 重点匹配*和:符号进行动态匹配，第一个匹配成功的节点用于插入，递归每一层如果没有 part 新建一个，匹配到末节点才会成功，所有匹配成功的点用于查找\nrouter handlers 存储每种请求方式，getroute 解析*和:，返回一个 map\ncontext 通过 c.Param(\u0026ldquo;lang\u0026rdquo;)获取值\n分组 将路径划分为组，需要知道父亲节点\n1 2 3 4 5 r := gee.New() v1 := r.Group(\u0026#34;/v1\u0026#34;) v1.GET(\u0026#34;/\u0026#34;, func(c *gee.Context) { c.HTML(http.StatusOK, \u0026#34;\u0026lt;h1\u0026gt;Hello Gee\u0026lt;/h1\u0026gt;\u0026#34;) }) gee 将 Engine 作为最顶层的分组，也就是说 Engine 拥有 RouterGroup 所有的能力\n中间件 通过调用(*Context).Next()函数，中间件可等待用户自己定义的 Handler 处理结束后，再做一些额外的操作，例如计算本次处理所用时间等 需要注意 next 的执行顺序，按照 handler 为准\ngee 定义 Use 函数到 group 可以给全局用也可以单独定义一个 group\n","date":"2023-05-07T00:00:00Z","permalink":"https://cancanneed64.github.io/p/7days-gee%E8%AE%B0%E5%BD%95/","title":"7days gee记录"},{"content":"Goroutine 入门 1. 并发 默认给整个应用程序只分配一个逻辑处理器，用于执行所有被创建的 goroutine 如果创建一个 goroutine 并准备运行，就会被放到调度器的全局运行队列中，之后，调度器就将这些队列中的 goroutine 分配给一个逻辑处理器，并放到这个逻辑处理器对应的本地运行队列，本地运行队列的 goroutine 会一直等到自己被分配的逻辑处理器执行\n2. 运行机制 M2 线程 -\u0026gt; 逻辑处理器 -\u0026gt; goroutine\nM3 线程 -\u0026gt; 逻辑处理器 -\u0026gt; goroutine\nM2 线程 -\u0026gt; 被阻塞的 goroutine\n当一个 goroutine 被阻塞，线程和 goroutine 会从逻辑处理器分离，线程继续阻塞，等待系统调用返回，逻辑处理器失去了用来运行的线程 调度器会创建新的线程并将其绑定到逻辑处理器上 调度器会从本地运行队列选择另一个 goroutine 来运行 一旦被阻塞的系统调用执行完，对应的 goroutine 会放回到本地运行队列，之前的线程会保存好以便继续使用 网络 I/O 调用流程不同\ngoroutine 会和逻辑处理器分离，并移到集成了网络轮询器的运行，一旦轮询器指示某个网络读或写准备就绪，对应的 goroutine 会重新分配到逻辑处理器上来完成\n3. 代码格式 调用 fmt/runtime/sync 分配一个逻辑处理器给调度器使用\nruntime.GOMAXPROCS(1)\nwg 用来等待程序完成\nvar wg sync.WaitGroup wg.Add(2)\n函数退出时 Done 通知 main 函数已完成\ngo func(){ defer wg.Done() }\n等待 goroutine 结束\nwg.Wait()\n创建两个 goroutine 打印前缀\ngo printPrime(\u0026quot;A\u0026quot;)\n给每个可用的核心分配一个逻辑处理器\nruntime.GOMAXPROCS(runtime.NumCPU())\n当前 goroutine 从线程退出，并放回到队列\nruntime.Gosched()\n竞争检测器\ngo bulid -race ./exmaple\ngo env set CGO_ENABLED=1\n1 2 Final Counter: 2 cgo: C compiler \u0026#34;gcc\u0026#34; not found: exec: \u0026#34;gcc\u0026#34;: executable file not found in %PATH% 安装 gcc\n4. 问题：竞争状态 对一个共享资源的读写操作必须是原子化的，一个时刻只能有一个 goroutine 对共享资源进行读写操作\ngoroutine 顺序 1 2 3 4 5 6 7 8 9 10 11 12 for _, name := range names { go func() { p.Run(\u0026amp;np) fmt.Println(1) wg.Done() fmt.Println(2) }() } fmt.Println(3) wg.Wait() p.Shutdown() } 1 2 3 4 5 1 2 3 1 2 规范\n1 2 3 4 5 for i := 0; i \u0026lt; 5; i++ { waitGroup.Add(1) go func() {defer waitGroup.Done()} } waitGroup.Wait() 原子函数调用 sync/atomic 和 sync\n原子函数以底层的加锁机制来同步访问整型变量和指针\ncounter 是所有 goroutine 都要增加其值的变量\nvar(counter int64)\n在 for 中添加函数同步整型值，当 goroutine 试图调用任何原子函数时，同一时刻只能有一个 goroutine 完成加法\natomic.AddInt64(\u0026amp;counter, 1)\natomic 中 LoadInt64 用来读取 shutdown 值，StoreInt64 用来修改 shutdown 值，如果哪个 dowork goroutine 试图在 main 函数调用 StoreInt64 的同时调用 LoadInt64 函数，那么原子函数会将这些调用互相同步，保证操作安全不进入竞争状态\n互斥锁\nmutex 保证同一时间只有一个 goroutine 可以执行临界区代码\nvar mutex sync.Mutex\nmutex.Lock()\nmutex.Unlock()\n5. 通道 make(chan int)\nmake(chan string, 10)\n向通道发送值或指针用\u0026lt;-\nchan \u0026lt;- \u0026quot;str\u0026quot;\n从通道接收值\nvalue := \u0026lt;-chan\n无缓冲通道要求接收方和发送方的 goroutine 都准备好发送和接受，否则阻塞\n有缓冲通道，只有在通道中没有要接收的值时，接收动作才会阻塞，只有在通道没有可用缓冲区容纳被发送的值时，发送动作才会阻塞\n当通道关闭后，goroutine 依旧可以接收数据，但是不能发送数据\n6. 总结 简单阐述了 go 调度器的运行机制，通过原子函数和互斥锁来强制锁住共享资源，解决资源竞争的问题\n而通道也可以发送接受共享资源，无缓冲和有缓冲通道的区别在于发送接受方式不同\n","date":"2023-05-07T00:00:00Z","permalink":"https://cancanneed64.github.io/p/goroutine-%E5%85%A5%E9%97%A8/","title":"Goroutine 入门"},{"content":"Goroutine 并发模式 1. runner 简介，使用通道监视程序执行时间，当开发需要调度后台处理任务的程序的时候，这个程序可能会座位 cron 作业执行，或者基于定时任务的云环境执行\n调度运行无人值守的面向任务程序，支持以下：\n程序可以在分配的时间内完成工作，正常终止； 程序没有及时完成工作，\u0026ldquo;自杀\u0026rdquo; 接受到操作系统发送的中断事件，程序立刻试图清理状态并停止工作 调用 errors/ os/signal /time/ os 声明 interrupt，complete，timeout 三个通道\ninterrupt chan os.Signal 从主机操作系统接收中断事件 signal 抽象了不同操作系统上捕获和报告信号事件的具体实现 complete chan error，complete 收发 error 接口类型值，被执行任务的 goroutine 用来发送任务已完成信号，返回 error 接口类型值或 nil，声明 ErrTimeout 和 ErrInterrupt timeout \u0026lt;-chan time.Time，管理执行任务时间，收到值后会试图清理状态并停止工作 tasks []func(int)，tasks 是一个函数值切片，表示执行顺序 初始化过程 interrupt 缓冲区容量为 1 的通道，保证通道至少能接受一个来自语言运行时的 os.Signal 值，如果 goroutine 没有准备好接受这个值就会被丢弃，ctrl+c 程序只会在这个通道的缓冲区可用时接受事件，其余所有事件被丢弃\ngotInterrupt 检查是否有要从操作系统接收的事件\n没有任何要接收的数据返回 false，有中断信号接收则调用 signal.Stop()之后返回 true\n主流程 Start，匿名函数启动 goroutine，将 run 的 error 接口值发到 complete 通道，如果 complete 接收到 error 表示中断或完成返回，否则超时返回\n主函数 main，规定 timeout，加入执行任务，执行任务处理结果，返回一个根据 id 休眠指定秒数\n主要负责处理超时/报错/加入多个任务\nrunner 展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 package runner import ( \u0026#34;errors\u0026#34; \u0026#34;os\u0026#34; \u0026#34;os/signal\u0026#34; \u0026#34;time\u0026#34; ) var ErrTimeOut = errors.New(\u0026#34;执行者执行超时\u0026#34;) var ErrInterrupt = errors.New(\u0026#34;执行者被中断\u0026#34;) /* runner 可以执行任何程序，可以监控程序，可以发送信号终止程序 */ type Runner struct { interrupt chan os.Signal //发送的信号，用来终止程序 complete chan error //用于通知任务全部完成 timeout \u0026lt;-chan time.Time //程序的超时时间 tasks []func(int) //要执行的任务 } //工厂函数 func New(tm time.Duration) *Runner { return \u0026amp;Runner{ complete: make(chan error), //无缓冲 timeout: time.After(tm), interrupt: make(chan os.Signal, 1), //有缓冲 } } //将需要执行的任务，添加到Runner里 func (r *Runner) Add(tasks ...func(int)) { r.tasks = append(r.tasks, tasks...) } //执行任务，执行的过程中接收到中断信号时，返回中断错误 //如果任务全部执行完，还没有接收到中断信号，则返回nil func (r *Runner) run() error { for id, task := range r.tasks { if r.isInterrupt() { return ErrInterrupt } task(id) } return nil } //检查是否接收到了中断信号 func (r *Runner) isInterrupt() bool { select { case \u0026lt;-r.interrupt: signal.Stop(r.interrupt) return true default: return false } } //开始执行所有任务，并且监视通道事件 func (r *Runner) Start() error { //希望接收哪些系统信号 signal.Notify(r.interrupt, os.Interrupt) go func() { r.complete \u0026lt;- r.run() }() select { case err := \u0026lt;-r.complete: return err case \u0026lt;-r.timeout: return ErrTimeOut } } main 展开代码 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 32 33 34 35 36 package main import ( \u0026#34;log\u0026#34; \u0026#34;os\u0026#34; \u0026#34;runner\u0026#34; //这个是写的runner所在包 \u0026#34;time\u0026#34; ) //必须在3内完成，超时时间 const timeout = 3 * time.Second func main() { log.Println(\u0026#34;开始工作\u0026#34;) r := runner.New(timeout) //初始化runner r.Add(createTask(), createTask(), createTask()) //添加任务 //执行任务并处理结果 if err := r.Start(); err != nil { switch err { case runner.ErrTimeOut: log.Println(\u0026#34;超时错误\u0026#34;) os.Exit(1) case runner.ErrInterrupt: log.Println(\u0026#34;中断错误\u0026#34;) os.Exit(2) } } log.Print(\u0026#34;工作完成\u0026#34;) } func createTask() func(int) { return func(id int) { log.Printf(\u0026#34;正在执行任务#%d \u0026#34;, id) time.Sleep(time.Duration(id) * time.Second) } } 2. pool 1.6 后标准库自带资源池 sync.Pool\n简介，如何使用有缓冲的通道实现资源池，管理任意数量 goroutine 之间共享及独立使用的资源 作用:共享数据库连接或者内存缓冲区，申请与归还资源到池中\n调用 errors/log/io/sync\nm sync.Mutex，保证多个 goroutine 访问资源池时值安全 resources chan io.Closer，有缓冲通道保存共享资源，池可以管理任意实现了 io.Closer 接口的资源类型 factory func() (io.Closer, error)，任何一个没有输入参数且返回一个 io.Closer 和 error 接口值的函数，当池需要一个新资源时可以用这个函数创建 closed bool，表示池是否被关闭，ErrPoolClosed New 工厂函数对接口进行初始化，fn 声明为一个函数类型不接受任何参数，返回一个 io.Closer 和一个 error 接口值，resources 创建一个有缓冲的管道，return 可以构造并初始化任何值\nAcquire 方法在还有可用资源时从资源池返回一个资源，否则为该调用创建并返回一个新的资源，select/case 检查通道是否还有资源，有则返回资源，无则 default 工厂函数创建并返回新资源，如果不需要已经获得的资源要释放回资源池里\nclose 会让资源池停止工作，并关闭所有现有资源，将进程池上锁，检查进程池关闭，在清空通道资源前先关闭，否则发生死锁，关闭资源\nRelease 传入进程池，返回资源和 io.Closer，加锁保证本操作和 Close 操作的安全，如果池被关闭则关闭资源，select/case 尝试把资源放进队列，队列满则关闭资源\n初始化过程，maxGoroutines 定义要使用的 goroutine 数量，pooledResources 定义池中资源数量，idCounter 分配连接 id，\n主函数 main，创建管理连接的池，使用池里的连接来完成查询，查询值是副本，不然所有的查询会共享同一个查询变量，等待 goroutine 结束关闭池\n管理池里的资源，负责获得/查询/关闭/连接资源\npool 展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 package pool import ( \u0026#34;errors\u0026#34; \u0026#34;io\u0026#34; \u0026#34;log\u0026#34; \u0026#34;sync\u0026#34; ) //一个安全的资源池，被管理的资源必须都实现io.Close接口 type Pool struct { m sync.Mutex res chan io.Closer factory func() (io.Closer, error) closed bool } var ErrPoolClosed = errors.New(\u0026#34;资源池已经被关闭。\u0026#34;) //创建一个资源池 func New(fn func() (io.Closer, error), size uint) (*Pool, error) { if size \u0026lt;= 0 { return nil, errors.New(\u0026#34;size的值太小了。\u0026#34;) } return \u0026amp;Pool{ factory: fn, res: make(chan io.Closer, size), }, nil } //从资源池里获取一个资源 func (p *Pool) Acquire() (io.Closer,error) { select { case r,ok := \u0026lt;-p.res: log.Println(\u0026#34;Acquire:共享资源\u0026#34;) if !ok { return nil,ErrPoolClosed } return r,nil default: log.Println(\u0026#34;Acquire:新生成资源\u0026#34;) return p.factory() } } //关闭资源池，释放资源 func (p *Pool) Close() { p.m.Lock() defer p.m.Unlock() if p.closed { return } p.closed = true //关闭通道，不让写入了 close(p.res) //关闭通道里的资源 for r:=range p.res { r.Close() } } func (p *Pool) Release(r io.Closer){ //保证该操作和Close方法的操作是安全的 p.m.Lock() defer p.m.Unlock() //资源池都关闭了，就省这一个没有释放的资源了，释放即可 if p.closed { r.Close() return } select { case p.res \u0026lt;- r: log.Println(\u0026#34;资源释放到池子里了\u0026#34;) default: log.Println(\u0026#34;资源池满了，释放这个资源吧\u0026#34;) r.Close() } } main 展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 package main //1.6版本之后自带pool import ( \u0026#34;log\u0026#34; \u0026#34;math/rand\u0026#34; \u0026#34;sync\u0026#34; \u0026#34;sync/atomic\u0026#34; \u0026#34;time\u0026#34; ) const ( //模拟的最大goroutine maxGoroutine = 5 ) func main() { //等待任务完成 var wg sync.WaitGroup wg.Add(maxGoroutine) p:=\u0026amp;sync.Pool{ New:createConnection, } //模拟好几个goroutine同时使用资源池查询数据 for query := 0; query \u0026lt; maxGoroutine; query++ { go func(q int) { dbQuery(q, p) wg.Done() }(query) } wg.Wait() } //模拟数据库查询 func dbQuery(query int, pool *sync.Pool) { conn:=pool.Get().(*dbConnection) defer pool.Put(conn) //模拟查询 time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond) log.Printf(\u0026#34;第%d个查询，使用的是ID为%d的数据库连接\u0026#34;, query, conn.ID) } //数据库连接 type dbConnection struct { ID int32//连接的标志 } //实现io.Closer接口 func (db *dbConnection) Close() error { log.Println(\u0026#34;关闭连接\u0026#34;, db.ID) return nil } var idCounter int32 //生成数据库连接的方法，以供资源池使用 func createConnection() interface{} { //并发安全，给数据库连接生成唯一标志 id := atomic.AddInt32(\u0026amp;idCounter, 1) return \u0026amp;dbConnection{ID:id} } 3. work 简介，使用无缓冲通道来创建一个 goroutine 池，保证两个 goroutine 间数据交换，不会卡住丢失\nPool 包含 Worker 接口和声明 sync wg New 使用固定数量的 goroutine 来新建一个工作池，创建一个 Pool 并用无缓冲通道初始化 work，创建同数 goroutine，只接收 worker 接口值并调用 Task 方法 循环先阻塞，直到将 worker 传到 work 通道中再执行 Task 方法 Run 方法向池里提交工作，调用者必须等待工作池某个 goroutine 收到这个值才会返回 最后再 shutdown 关闭 work 通道，调用 wg.wait\nname 声明了名字切片，并声明 namePrinter 类，实现 Task 接口打印名字并等待一秒 调用 work 包的 New 函数创建工作池，包含两个执行任务的 goroutine，name 切片每个名字会创建 100 个 goroutine 提交任务 每次内部循环都会创建 namePrinter 类型值并提供一个打印名字，声明了一个匿名函数创建一个 goroutine 执行，这个 goroutine 调用 run 将值提交到池，run 方法再返回，wg 计数减少，最终 shutdown\nworker 展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package work import ( \u0026#34;sync\u0026#34; ) // 任务类型接口 type Worker interface { Task(goid int) } // 任务池 type Pool struct { work chan Worker wg sync.WaitGroup } // 新建 func New(maxGoroutines int) *Pool { //任务池 p := Pool{ work: make(chan Worker), } p.wg.Add(maxGoroutines) //创建maxGoroutines个go协程 for i := 0; i \u0026lt; maxGoroutines; i++ { go func(goid int) { //保证goroutine不停止执行通道中的任务 for w := range p.work { w.Task(goid) } //每个goroutine不再执行work通道中任务时停止 p.wg.Done() }(i) } return \u0026amp;p } // 运行 func (p *Pool) Run(r Worker) { p.work \u0026lt;- r } // 停止 func (p *Pool) Shutdown() { close(p.work) p.wg.Wait() } main 展开代码 ","date":"2023-05-07T00:00:00Z","permalink":"https://cancanneed64.github.io/p/goroutine%E5%B9%B6%E5%8F%91%E6%A8%A1%E5%BC%8F/","title":"Goroutine并发模式"},{"content":"goweb 入门 工具 nc 命令 nc 用法\n调试热加载 热加载\nfresh go get -v -u github.com/pilu/fresh go install\nair go get -u github.com/cosmtrek/air\n处理请求 curl 使用说明\npowershell 中需要使用 curl.exe\nRESTful API 通过调用\u0026quot;net/http\u0026quot;和\u0026quot;github.com/gin-gonic/gin\u0026quot;\n定义 album 类，实例化\n用 router:=gin.Default(),去调用 get/post 和定义的其他方法\n通讯问题 字符显示问题 chcp 65001\n打开 cmd 运行可解决部分错误\n输入秒发送 逻辑\nserver 监听端口 go 监听消息，将信息发给 usermap 接受 socket，处理信息 新版客户端 没有存在字符问题 但 rename 和 who 有问题 存疑 client 中的模式处理 user 中的 who 处理\n44 集踢出问题 在 handle 函数中时间超过后打印被踢信息，并且关闭资源连接，返回 start 函数 但是在 start 函数中一直在 for 循环监听 listener，因此会占用资源 通过排错，发现 server 问题\n打印错误\n1 2 3 4 5 _, err := this.conn.Write([]byte(msg + \u0026#34;\\n\u0026#34;)) if err != nil { fmt.Println(err) return } panic 用法\n1 2 3 4 5 6 7 8 9 10 11 12 func main() { test() } func test() { defer func() { if err := recover(); err != nil { fmt.Printf(\u0026#34;recover:%v\\n\u0026#34;, err) } }() panic(\u0026#34;an error occurred\u0026#34;) } for-range channel 用法 当 channel 关闭时，for 循环会自动退出，无需主动检测 channel 是否关闭，防止读取已关闭的 channel，造成读取到数据为通道所存储的数据类型的零值\n基础 数据模型 User，session，thread，post 和数据库交互，处理器返回数据给模板引擎，再传给模板\n通过 URL 进行接受请求\n多路复用器 mux := http.NewServeMux() net/http，将收到的请求重定向到处理器\n将发送至根 URL 的请求重定向到处理器 mux.HandleFunc(\u0026quot;/\u0026quot;, index) 所有处理器都接受一个 ResponseWriter 和一个指向 Request 结构的指针作为参数，并且所有请求参数通过访问 Requset 结构得到，所以程序不需要向处理器显式地传入任何请求参数\n服务静态文件 file := http.FileServer(http.Dir(\u0026quot;/public\u0026quot;)) mux.Handle(\u0026quot;/static/\u0026quot;, http.StripPrefix(\u0026quot;/static/\u0026quot;, files)) 删除/static/字符串在 public 目录里查找被请求的文件\n处理器函数，接受 ResponseWriter 和 Request 指针作为参数的函数\ncookie 服务器在响应首部写入 cookie，客户端接受 cookie 后把它存储到浏览器里，route_auth.go 的 authenticate 处理器函数\nsession 记录各项信息存储到数据库，决定访问 public 或 private 页面 utility 包 判断 cookie 存在，data.Session 的 check 方法访问数据库校对唯一 ID 存在，index 函数获取 err 变量判断用户是否已经登录\n使用模板生成 HTML 响应 用{{define \u0026ldquo;\u0026rdquo;}}标识动作 ParseFiles 函数对模板进行语法分析并创建出相应模板，Must 函数包围 ParseFiles 函数，返回错误报告\n(.)代表了传递给被引用模板的数据 {{.Topic}}访问的是 Thread 结构的 Topic 字段，在访问字段时必须在字段名的前面加上点号，并且字段名的首字母必须大写\n安装 PostgreSQL 数据库创建\n查看数据库 \\l\n表格创建\npq 包虽然用_忽略，但实际上连接数据库还是会调用\npsql -f setup.sql -d chitchat 无法生效 手动创建数据库\n在 chitchat 目录下运行 go build，报 data 包错误，直接将 data 丢入 src 路径，go mod tidy 解决\n添加项目\nSSL 处理器与处理器函数 任何实现了 ServeHTTP 的类型都可以作为 HTTP 请求的处理器\n处理器拥有 ServeHTTP 方法接口，ServeHTTP 需要接受两个参数，ResponseWriter 和 Request 指针\n1 2 3 4 5 6 7 8 9 10 11 12 type Handler interface { ServeHTTP(ResponseWriter, *Request) } // 自定义类型实现 Handler 接口 type MyHandler struct{} func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Write([]byte(\u0026#34;Hello from Handler!\u0026#34;)) } // 注册处理器 http.Handle(\u0026#34;/path\u0026#34;, \u0026amp;MyHandler{}) 允许普通函数直接作为处理器使用，无需定义新类型。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 type HandlerFunc func(ResponseWriter, *Request) // 实现 ServeHTTP 方法（将函数转为 Handler） func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) // 直接调用自身 } // 普通函数 func helloHandler(w http.ResponseWriter, r *http.Request) { w.Write([]byte(\u0026#34;Hello from HandlerFunc!\u0026#34;)) } // 将函数转换为 HandlerFunc 类型，再注册为处理器 http.Handle(\u0026#34;/hello\u0026#34;, http.HandlerFunc(helloHandler)) // 更简洁的写法（直接使用 HandleFunc） http.HandleFunc(\u0026#34;/hello\u0026#34;, helloHandler) 处理器函数与处理器有相同行为，与 ServeHTTP 方法拥有相同签名 func hello(w http.ResponseWriter, r *http.Requset) http.HandleFunc(\u0026quot;/hello\u0026quot;, hello)\nHandleFunc 可以把一个带有正确签名的函数 f 转换成一个带有方法 f 的 Handler，并与 DefaultServeMux 进行绑定\n处理器函数不能替代处理器，代码包含了某个接口或者某种类型，需要为它们添加 ServeHTTP 方法转变为处理器\n串联处理器和处理器函数\nServeMux 请求多路复用器，DefaultServeMux 是 ServeMux 一个实例，用户没有指定处理器则会使用\nHTTP2 http2.ConfigureServer(\u0026amp;server, \u0026amp;http2.Server{}) curl -I --http2 --insecure https://localhost:8080/\n实际应用在中间件中 通过 HandlerFunc 链式调用实现中间件\n1 2 3 4 5 6 7 8 9 func loggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Println(\u0026#34;Request received:\u0026#34;, r.URL.Path) next.ServeHTTP(w, r) // 调用下一个处理器 }) } // 使用中间件包装处理器 http.Handle(\u0026#34;/secure\u0026#34;, loggingMiddleware(\u0026amp;AuthHandler{})) 在 Gin 中，处理器函数通常是 gin.HandlerFunc 类型，与标准库思想类似\n1 2 3 4 5 6 func ginHandler(c *gin.Context) { c.String(200, \u0026#34;Hello Gin!\u0026#34;) } router := gin.Default() router.GET(\u0026#34;/gin\u0026#34;, ginHandler) // 直接使用函数 表单 enctype 属性 简单使用 application/x-www-form-urlencoded 不同得键值对将使用\u0026amp;符号分隔，键和值用=分隔 first_name=sau%20sheong\u0026amp;last_name=chang\n上传文件使用 mutipart/form-data 表单中得键值对带有各自的内容类型和配置 ----WebKit.... Content-Disposition:form-data;name=\u0026quot;firstname\u0026quot; sau sheong\n分析字段 以下三者围绕着返回值和表单属性进行区分 requset 将 url/主体等提取到 form/postform/multipartform 字段中，调用 parseform 方法或者 parsemultipartform 方法进行解析\nForm 字段 r.ParseForm() fmt.Fprintln(w, r.Form) 将获得一个切片，包含键的表单值或者 URL 值 map[thread:[123] hello:[sau sheong world] post:[456]]\nPostForm 字段 r.PostForm() 将获得一个键的表单值不包含 URL 值 只支持 urlencoded 编码\napplication/x-www-form-urlencoded 下 PostForm，获得表单值 map[post:[456] hello:[sau sheong]]\n修改成 multipart/form-data 下使用回 r.Form，获得 url 值 map[hello:[world] thread:[123]]\nMutipartForm 字段 只包含表单不包含 URL 键值对，包含两个映射，一个为字符串组成切片，一个为空，为上传文件\nr.ParseMultipartForm(1024) fmt.Fprintln(w, r.MultipartForm)\n\u0026amp;{map[hello:[sau sheong] post:[456]] map[]}\nFormValue 字段 允许直接访问与给定键相关联的值 FormValue 自动调用 ParseForm 或 ParseMultipartForm 方法，但指挥从 Form 结构取出给定键的第一个值，要获取全部需要直接访问 Form 结构\nPostFormValue 字段 PostFormValue 同理，只会返回表单不会返回 url，两者都只支持 application/x-www-form-urlencoded，在 multipart/form-data 中不会得到任何结果 multipart/form-data 时，数据被存储到 MultipartForm 字段中而不是 Form 字段和 PostForm 字段\n上传文件 前端 enctype=\u0026quot;multipart/form-data\u0026quot; \u0026lt;input type=\u0026quot;file\u0026quot; name=\u0026quot;uploaded\u0026quot;\u0026gt;\n后端\n1 2 3 r.ParseMultipartForm(1024) fileHeader := r.MultipartForm.File[\u0026#34;uploaded][0] file, err := fileHeader.Open() 执行 ParseMultipartForm 方法，从 MultipartForm 的 File 取出 fileHeader，然后通过调用文件头的 Open 方法打开文件，服务器会将文件的内容读取到一个字节数组中，并将这个字节数组的内容打印出来，纯文本文件会把这个文件内容打印在浏览器上 FormFile 可以返回给定键的第一个值，返回文件和文件头\nParseForm 方法无法从 Angular 客户端发送 POST 请求中获取 JSON 数据，使用的是 application/json，ParseForm 方法只对表单数据进行分析\nResponseWriter 实际上 ResponseWriter 就是 response 这个非导出结构的接口，传递的也是指向 response 结构的指针，包含 Write/WriteHeader/Header 三个方法 Write 可以将 HTML 字符串写入 HTTP 响应主题中 WriteHeader 接受一个代表 HTTP 响应状态码的整数作为参数，并将这个整数用作 HTTP 响应的返回状态码，用户可以继续对 ResponseWriter 进行写入但是不能对响应的首部做任何写入 Header 取得一个由首部组成的映射，修改映射可以修改首部，修改后的首部将被包含在 HTTP 响应里面，并随着响应一同发送至客户端\n重定向方法 w.Header().Set(\u0026quot;Location\u0026quot;, \u0026quot;http://www.baidu.com\u0026quot;) w.WriteHeader(302) 给响应首部添加一个 Location，并设置为重定向目的地，WriteHeader 执行后不再允许写入\nCookie 除了 Expires 字段，还有 MaxAge 字段\n设置 cookie\n1 2 3 w.Header().Set(\u0026#34;Set-Cookie\u0026#34;, c1.String()) w.Header().Add(\u0026#34;Set-Cookie\u0026#34;, c2.String()) http.SetCook(w, \u0026amp;c1) //传递指向Cookie结构的指针 cookie 实现闪现消息 c := http.Cookie{ Name: \u0026ldquo;flash\u0026rdquo;, Value:base64.URLEncoding.EncodeToString(msg) }\nrc := http.Cookie{ Name: \u0026ldquo;flash\u0026rdquo; MaxAge: -1 Expires: time.Unix(1, 0) } val, _ := base64.URLEncoding.DecodeString(c.Value)\n通过完全移除 cookie，程序对旧 cookie 解码并返回\n5. 模板引擎 无逻辑模板引擎，将模板中指定的占位符替换成相应的动态数据，完全分离程序的表现和逻辑，计算交给处理器完成\n嵌入逻辑的模板引擎，将编程语言嵌入模板并在模板引擎渲染模板时，由代码进行相应的字符串替换\n模板中默认动作使用{{}}包围，而点(.)是一个动作，模板引擎执行模板时，使用一个值去替换动作本身 步骤： (1) 对文本格式模板源进行语法分析，创建一个经过语法分析的模板结构，模板源既可以是一个字符串，也可以是模板文件中包含的内容； (2) 执行经过语法分析的模板，将 ResponseWriter 和模板所需的动态数据传递给模板引擎，被调用的模板引擎会把经过语法分析的模板和传入的数据结合起来，生成出最终的 HTML，并将这些 HTML 传递给 ResponseWriter\nt, _ := template.ParseFiles(\u0026quot;tmpl.html\u0026quot;) t.Execute(w, \u0026quot;Hello World!\u0026quot;) 等同于 t := template.New(\u0026quot;tmpl.html\u0026quot;) t, _ := t.ParseFiles(\u0026quot;tmpl.html')\nParseFiles 可以接受多个文件名作为参数，变成集合，但只返回第一个文件的已分析模板 ParseGlob 会对匹配给定模式的所有文件进行语法分析 t, _ := template.ParseGlob(\u0026quot;*.html\u0026quot;)\n处理分析模板时出现的错误 t := template.Must(template.ParseFiles(\u0026quot;tmpl.html\u0026quot;)) Must 函数可以包裹起一个函数，返回一个指向模板的指针和一个错误，如果不是 nil 则产生 panic panic 会终止正常流程，如果 panic 是内部函数产生那会返回给调用者，一直向调用栈的上方传递，直到 main 函数\n如果想执行别的模板需要 t.ExecuteTemplate(w, \u0026quot;t2.html\u0026quot;, \u0026quot;Hello World\u0026quot;)\n动作 条件动作/迭代动作/设置动作/包含动作\nt.Execute(w, )负责传入\n1 2 3 {{if .}} action {{else}} action {{end}} 1 2 3 4 {{range .}} \u0026lt;li\u0026gt;{{.}}\u0026lt;/li\u0026gt; {{else}} action {{end}} 1 2 3 4 5 {{with arg}} 到end之间.会变成arg的内容 {{else}} action 如果arg为空，则会替换成else {{end}} 包含另一个模板 {{template \u0026ldquo;name\u0026rdquo;}}\nParseFiles 第一个参数有特殊作用，传递给第二个模板需要用{{template \u0026ldquo;t2.html\u0026rdquo; .}}，可以把 t1.html 的{{.}}传递给 t2.html\n变量$variable := value 管道可以传递给下一个参数\n自定义模板函数 (1)创建一个名为 FuncMap 映射并将映射的键设置为函数名字，而映射的值则设置为实际定义函数 (2)将 FuncMap 与模板进行绑定 用户常常需要将时间对象或日期转换为 ISO8601 格式的时间字符串或者日期字符串\n定义函数返回 t.Format(layout),处理器中创建一个变量名为 funcMap，使用结构将名字 fdate 映射至 formatDate 函数，template.New 函数创建一个名为 tmpl.html 的模板，funcMap 传递给 template.New 返回被创建模板进行绑定，对 tmpl.html 进行语法分析，将 ResponseWriter 以及当前时间传递给模板\nfuncMap := template.FuncMap{\u0026quot;fdate\u0026quot;: formatDate} t := template.New(\u0026quot;tmpl.html\u0026quot;).Funcs(funcMap) t, _ = t.ParseFiles(\u0026quot;tmpl.html\u0026quot;) t.Execute(w, time.Now())\n上下文感知，对 HTML/JS 等进行转义 w.Header().Set(\u0026quot;X-XSS-Protection\u0026quot;, \u0026quot;0\u0026quot;)\n介于标签之间的内容就是 layout 模板 {{define \u0026ldquo;layout\u0026rdquo;}} {{end}}\nt.ExecuteTemplate(w, \u0026quot;layout\u0026quot;, \u0026quot;\u0026quot;)\n块动作定义默认模板，可以将 content 放置到 layout 中 {{block arg}} {{end}}\n构建运行 1.将文件夹复制到\u0026quot;C:\\Program Files\\Go\\src\u0026quot;中 2.管理员权限开 cmd 运行 go install 目录名 3.可以在 bin 目录里找到 exe，运行\n常见问题： 端口被占用无法运行 netstat -ano | findstr 查看端口 taskkill /PID pid /f 关闭端口\ncurl -i 返回请求头 -d 提交 Post\n存储数据 gob 一种能存储在文件里面的二进制格式，可以快速高效地将内存中的数据序列化到一个或多个文件里面\nio/ioutill 库 用 WriteFile 和 ReadFile 对文件进行写入读取\n1 2 ioutill.WriteFile(\u0026#34;data1\u0026#34;, data, 0644) read1, _ := ioutil.ReadFile(\u0026#34;data1\u0026#34;) 写入程序会将文件的名字/数据以及权限传入 读取将文件名做参，返回一个由字节组成的切片\nos 库 通过 File 结构对文件进行写入读取\n1 2 3 4 5 6 7 file1, _ := os.Create(\u0026#34;data2\u0026#34;) defer file1.Close() bytes, _ := file1.Write(data) file2, _ := os.Open(\u0026#34;data2\u0026#34;) defer file2.Close() read2 := make([]byte, len(data)) bytes, _ = file2.Read(read2) Create 出 file 文件后进行写入，Open 文件后进行 read\nCSV 处理 创建写入器，把数据创建为一个由字符串组成的切片，Flush 方法保证缓冲区数据写入\n1 2 3 writer := csv.NewWriter(csvFile) ... writer.Flush() 打开 csv 文件，将 FieldsPerRecord 字段设为-1，即使读取时缺少字段也不会中断，为正数时，字段数量少于值会报错，为 0 时，读取第一条记录的字段数量未作值，在使用 readall 一次性读取所有记录\n1 2 3 4 5 6 7 file, err := os.Open(\u0026#34;csv\u0026#34;) ... defer file.Close() reader := csv.NewReader(file) reader.FiledsPerRecord = -1 record, err := reader.ReadAll() ... gob 包 通过二进制读取存储数据 存储数据\n1 2 3 4 5 func store(data interface{}, filename string){ buffer := new(bytes.Buffer) encoder := gob.NewEncoder(buffer) ... } 载入数据\n1 2 3 4 5 6 func load(data interface{}, filename string){ raw, err := ioutil.ReadFile(filename) buffer := bytes.NewBuffer(raw) dec := dec.Decode(data) ... } Postgres CRUD 导入驱动，调用库 _ \u0026quot;github.com/lib/pq\u0026quot; \u0026quot;database/sql\u0026quot; 隐形调用 sql.Register(\u0026ldquo;postgres\u0026rdquo;, \u0026amp;drv{})\n创建用户，-P 密码，-d 权限 createuser -P -d gwp\n创建数据库 createdb gwp\n创建表格 psql -U gwp -f setup.sql -d gwp\n连接数据库 Db, err = sql.Open(exe, user dbname password sslmode)\n如果密码不对，则会产生权限问题，无法对数据库进行操作 defer Db.Close()放在 main 函数中否则占用资源 惰性连接\n详细说明 有了 sql.DB 实例之后就可以开始执行查询语句了。\nGo 将数据库操作分为两类：Query 与 Exec。两者的区别在于前者会返回结果，而后者不会。\nQuery 表示查询，它会从数据库获取查询结果（一系列行，可能为空）。 Exec 表示执行语句，它不会返回行。 此外还有两种常见的数据库操作模式：\nQueryRow 表示只返回一行的查询，作为 Query 的一个常见特例。 Prepare 表示准备一个需要多次使用的语句，供后续执行用 Scan 方法把行中的值复制到程序为其提供的参数里面，一般与 QueryRow 搭配使用\n查询数据，传入 limit 限制行数\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func Posts(limit int) (posts []Post, err error) { rows, err := Db.Query(\u0026#34;select id, content, author from posts limit $1\u0026#34;, limit) if err != nil { return } for rows.Next() { post := Post{} err = rows.Scan(\u0026amp;post.Id, \u0026amp;post.Content, \u0026amp;post.Author) if err != nil { return } posts = append(posts, post) } rows.Close() return } 单一查询\n1 2 3 4 5 func GetPost(id int) (post Post, err error) { post = Post{} err = Db.QueryRow(\u0026#34;select id, content, author from posts where id = $1\u0026#34;, id).Scan(\u0026amp;post.Id, \u0026amp;post.Content, \u0026amp;post.Author) return } 增加\n1 2 3 4 5 6 7 8 9 10 func (post *Post) Create() (err error) { statement := \u0026#34;insert into posts (content, author) values ($1, $2) returning id\u0026#34; stmt, err := Db.Prepare(statement) if err != nil { return } defer stmt.Close() err = stmt.QueryRow(post.Content, post.Author).Scan(\u0026amp;post.Id) return } 删除\n1 2 3 4 func (post *Post) Delete() (err error) { _, err = Db.Exec(\u0026#34;delete from posts where id = $1\u0026#34;, post.Id) return } 改变\n1 2 3 4 func (post *Post) Update() (err error) { _, err = Db.Exec(\u0026#34;update posts set content = $2, author = $3 where id = $1\u0026#34;, post.Id, post.Content, post.Author) return } 尝试将其他搜索条件传入，返回 id 值\n7.go web SOAP 响应报文由 WSDL 生成的 SOAP 服务器负责，每次修改服务器，即使是修改返回值类型客户端也需要重新生成\nREST 对象模型表示事物，函数称为方法，以资源的形式把模型暴露出来，人们通过少数几个称为动词的动作来操纵资源 HTTP 实现 REST 服务时，URL 用于表示资源，HTTP 方法则操纵资源动词\nPUT 再使用时需要知道哪项资源会被替换，POST 则只会创建出一项新资源以及一个新 URL PUT 是幂等的，无论调用多少次服务器的状态都不会改变，PUT 会重复修改一项资源\nREST 对资源执行动作方法 把过程具体化，把动作转为名词然后用作资源 把动作用作资源的属性 优点是可以添加额外属性，再用 PATCH 对资源进行部分更新\nXML 创建用于存储 XML 数据的结构 使用 unmarshal 将 xml 数据解封\n在结构中使用`作为标签，名字必须以大写英文字母开头，创建一个与 XML 元素标签同名的字段存储，可以将元素\u0026quot;\u0026lt;/name/\u0026gt;\u0026ldquo;属性存到字段，也可以使用 a\u0026gt;b\u0026gt;c 形式\n解码 处理体积小的 XML 文件,unmarshal\n1 2 3 4 5 6 7 xmlFile, err := os.Open(\u0026#34;post.xml\u0026#34;) ... defer xmlFile.Close() xmlData, err := ioutil.ReadAll(xmlFile) ... var post Post xml.Unmarshal(xmlData, \u0026amp;post) 以流的方式传输 XML 文件以及体积较大的文件 decoder\n1 2 3 4 5 6 xmlFile, err := os.Create(\u0026#34;post.xml\u0026#34;) ... encoder := xml.NewEncoder(xmlFile) encoder.Indent(\u0026#34;\u0026#34;, \u0026#34;\\t\u0026#34;) err = encoder.Encode(\u0026amp;post) ... 创建 XML marshal\n1 2 output, err := xml.Marshal(\u0026amp;post) //无格式 output, err := xml.MarshalIndent(\u0026amp;post, \u0026#34;\u0026#34;, \u0026#34;\\t\\t\u0026#34;) //有格式 添加 XML 声明 err = ioutil.WriteFile(\u0026quot;post.xml\u0026quot;, []byte(xml.Header + string(output)), 0644)\nencode\n1 2 3 4 5 xmlFile, err := os.Create(\u0026#34;post.xml\u0026#34;) ... encoder := xml.NewEncoder(xmlFile) encoder.Indent(\u0026#34;\u0026#34;, \u0026#34;\\t\u0026#34;) ... JSON 创建存储结构-把 json 数据解封到结构 创建存储结构-创建用于解码的解码器-遍历整个 JSON 文件并将数据解码至结构\n创建存储结构并填充-把结构封装为 JSON 数据 创建存储结构并填充-创建出用于存储 JSON 数据的 JSON 文件-创建用于编码 JSON 数据的编码器-通过编码器把结构编码至 JSON 文件\n解码 marshal\n1 2 3 4 5 6 7 jsonFile, err := os.Open(\u0026#34;post.json\u0026#34;) ... defer jsonFile.Close() jsonData, err := ioutil.ReadAll(jsonFile) ... var post Post json.Unmarshal(jsonData, \u0026amp;post) encode\n1 2 3 4 5 6 7 8 9 10 jsonFile, err := os.Open(\u0026#34;post.json\u0026#34;) ... defer jsonFile.Close() decoder := json.NewDecoder(jsonFile) for { var post Post err := decoder.Decode(\u0026amp;post) ... } 创建 json marshal\n1 2 3 4 output, err := json.MarshalIndent(\u0026amp;post, \u0026#34;\u0026#34;, \u0026#34;\\t\\t\u0026#34;) ... err = ioutil.WriteFile(\u0026#34;post.json\u0026#34;, output, 0644) ... encode\n1 2 3 4 5 6 jsonFile, err := os.Create(\u0026#34;post.json\u0026#34;) ... jsonWriter := io.Writer(jsonFile) encoder := json.NewEncoder(jsonWriter) err = encoder.Encode(\u0026amp;post) ... 并发与并行 并发，多个任务在同一时间段内启动互动，任务通过通信分享数据并协调执行时间，共享一个资源\n并行，把大任务分割成小任务，需要独立资源，不会重叠处理\nGOMAXPROCS 让并行可以同时运行多个任务\n打印数字和英文 创建 go routine 程序 在 TestGoPrint1 中，如果规定 CPU 最大数量不为 1，每次打印的结果不相同，可以通过定义数量来固定结果 go test -run x -bench . -cpu 1 go test -v 需要通过延迟来显示结果 time.Sleep(1 * time.Millisecond)\n增加 CPU 的数量并不一定能带来性能提升\n等待执行完毕 1 2 3 4 5 6 7 8 9 10 11 func (wg *sync.WaitGroup){ ... wg.Done() } fuc main(){ var wg sync.WaitGroup wg.Add(2) go printNumbers2(\u0026amp;wg) go printLetters2(\u0026amp;wg) wg.Wait() } 如果没有对计数器进行减数操作会引发 panic\n通道 只能执行发送操作的字符串管道 ch := make(chan \u0026lt;- string)\n只能执行接收操作的字符串管道 ch := make(\u0026lt;-chan string)\n阻塞主程序，直到 w \u0026lt;- true 被触发 \u0026lt;-w1 main 函数尝试移除 w1 的值，但没有包含任何值因此阻塞\n无缓冲通道会轮番打印，但由于 print 抢先执行，会出现先取后入的情况\n有缓冲通道会一直运行直到通道满\nselect 语句允许从多个通道选择一个来操作，但不加上 default 会出现死锁 当 select 没有发现可用通道，会执行 default，如果不加延时，只会看见默认分支输出，通道 a 和 b 还没来得及接受值，select 就跳过执行\nclose 关闭通道并不是必须的，用于通知接收者该通道不会在收到任何值\n程序从通道取值是多值方式，值和通道的状态 case value, ok1 = \u0026lt;-a\n马赛克算法 键：图片的文件名，值：图片平均颜色， 通过计算图片每个像素红/绿/蓝 3 种颜色的总和，并将它们除以像素总数量，得到一个三元组，三元组计算图片的平均颜色 根据瓷砖图片大小切割目标图片 对目标图片的子图片计算位于左上方的第一个像素定义为平均颜色 根据子图片平均颜色，在瓷砖图片找出一张最为接近的然后替代。程序需要将子图片平均颜色与瓷砖图片平均颜色转化为三维空间的一个点并计算欧几里得距离 选中瓷砖图片后从数据库移除 ","date":"2023-05-07T00:00:00Z","permalink":"https://cancanneed64.github.io/p/goweb%E5%85%A5%E9%97%A8/","title":"Goweb入门"},{"content":"Go 常见问题 module 设置 修改代理 GOPROXY\ngo env -w GOPROXY=https://goproxy.cn,direct\n修改模式 GO111MODULE\ngo env -w GO111MODULE=on\n包引用 创建模块与调用 创建 2 个同级文件夹，go mod init 两个文件 将 Go 工具从其模块路径重定向到本地目录 同步模块的依赖项，添加代码所需的依赖项，但尚未在模块中跟踪的依赖项 go mod edit -replace example.com/greetings=../greetings go mod tidy go run .\n官网说明\n方法一 go mod edit 修改路径导入本地包 失败 go mod tidy\n方法二 放入路径 hello.go:6:2: package greetings is not in GOROOT (C:\\ProgramFiles\\Go\\src\\greetings) 需要管理员权限\n方法三 放入路径 %home%/src cd 进入并运行 go 文件\ngo get -u 才会拉取下载\ngodoc 创建目录下载包 如果创建在 program 目录下没有权限执行，因此创建在 gopath 下的 src\n1 2 3 4 cd $GOPATH/src/golang.org/x/tools/cmd/ go install ... go get -u golang.org/x/tools/cmd/godoc go install golang.org/x/tools/cmd/godoc get/install\ngo get/go mod vendor\n创建工作区 workspaces 初始化 go work init path\n生成 go.work\n1 2 3 go 1.18 use ./hello 在 workspace 的任何子目录中，模块都会被激活\ngo run example.com/hello\n需外网下载 git clone https://go.googlesource.com/example\n将模块添加到工作区 go work use ./example\n1 2 3 4 5 6 go 1.18 use ( ./hello ./example ) 这将允许我们使用将在 stringutil 模块副本中编写的新代码，而不是使用 go get 命令下载的模块缓存中的模块版本.\nworkspace/example/stringutil 目录中创建一个名为 toupper.go 的新文件运行测试\n发布模块 go get golang.org/x/example@v0.1.0\n删除不存在 use 目录，递归添加目录 go work use [-r] [dir] go work use -drop ./old-module\n编辑 work 文件 go work edit\n将列表中依赖同步到每个工作区 go work sync\n编译与安装应用程序 在目录中运行, 并允许可执行文件测试 go build 查询路径 go list -f 'package'\n添加 go 安装目录到系统路径中，这样就可以允许可执行文件无需指定路径 win set PATH=%PATH%;C:\\path\\to\\your\\install\\directory\n或者，如果您的 shell 路径中已经有一个目录（如 $HOME/bin），并且您希望在那里安装 Go 程序，则可以通过使用 go env 命令设置 GOBIN 变量来更改安装目标： go env -w GOBIN=C:\\path\\to\\your\\bin\n更新 shell 路径后，运行 go install 命令来编译和安装包，然后可直接允许 exe go install\n更换环境进行编译\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # 查看之前的golang编译环境 go env # 设置成linux对应的 set GOARCH=amd64 go env -w GOARCH=amd64 set GOOS=linux go env -w GOOS=linux ​ go build -o [目标可执行程序] [源程序] # 例子 go build -tags dev -o path main.go ​ # 还原之前的编译环境 ​ set GOARCH=amd64 go env -w GOARCH=amd64 set GOOS=windows go env -w GOOS=windows ","date":"2023-05-07T00:00:00Z","permalink":"https://cancanneed64.github.io/p/go%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98/","title":"Go常见问题"},{"content":"go 数据处理 关于 map 变量名以小写开头不公开，其他包可以间接调用，一个函数可以返回一个未公开类型的值 未公开标识符 定义类型，小写开头为未公开标识符 文件夹应该同包名相同 当需要调用未公开标识符时，可以创建一个工厂函数 New 来返回该未公开标识符\nmap 需要先用 make 构造，否则会报错，因为 map 变量默认零值 nil\ngo 打印中文出现乱码 来源\n普通 println 打印字符串显示正常，遍历字符串乱码，因为传统字符串遍历按字节遍历，而一个汉字占 3 个字节\n1 2 3 4 var str string = \u0026#34;halo,北京\u0026#34; for i := 0; i \u0026lt; len(str); i++ { fmt.Printf(\u0026#34;%c\u0026#34;, str[i]) } 方法一 使用 rune 切片可以\n1 2 3 4 5 var str string = \u0026#34;halo,北京\u0026#34; str2 := []rune(str) for i := 0; i \u0026lt; len(str2); i++ { fmt.Printf(\u0026#34;%c\u0026#34;, str2[i]) } 方法二 使用 range，会跳过 3 个 index 打印字\n1 2 3 4 var str string = \u0026#34;halo,北京\u0026#34; for index, val := range str { fmt.Printf(\u0026#34;index=%d, val=%c \\n\u0026#34;, index, val) } main 展开代码 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 package main import ( \u0026#34;fmt\u0026#34; \u0026#34;unsafe\u0026#34; ) func main() { //byte代表 UTF-8 字符串的1个字节的值，表示一个ASCII码字符,utf-8中一个中文占三个字节 //rune（实际上是int32），代表单个 Unicode字符4个字节，常用来处理unicode或utf-8字符（一切字符） //Go 语言的 string 是用 uft-8 进行编码的，英文字母占用一个字节，而中文字母占用 3个字节 //%q解释型字符串，反引号可以不写换行符（因为没法写）来表示一个多行的字符串 s1 := \u0026#34;big\u0026#34; fmt.Println(s1) bytestr := []byte(s1) bytestr[0] = \u0026#39;p\u0026#39; fmt.Printf(\u0026#34;%T\u0026#34;, bytestr) //uint8 fmt.Println(bytestr) runestr := []rune(s1) runestr[0] = \u0026#39;p\u0026#39; fmt.Printf(\u0026#34;%T\u0026#34;, runestr) //int32 fmt.Println(runestr) fmt.Printf(\u0026#34;byte 占用 %d 个字节数\\nrune 占用 %d 个字节数\u0026#34;, unsafe.Sizeof(bytestr[0]), unsafe.Sizeof(runestr[0])) } URI url 编码会把保留字符转换成在 ASCII 中对应得字节值 byte，接着表示为两位长的十六进制数字，并在前面加上百分号\n泛型 func SumInts(m map[string]int64) int64 SumFloats 将 string 映射为 float64 值\n泛型函数 func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V\n简化为 func SumNumbers[K comparable, V Number](m map[K]V) V\ncomparable 的约束在 Go 中预先声明。它允许其值可用作比较运算符== 和!= 的操作数的任何类型。Go 要求映射键具有可比性。因此，将 K 声明为 comparable 是必要的，以便您可以使用 K 作为映射变量中的键。它还确保调用代码对映射键使用允许的类型 Go 要求映射键具有可比性。因此，将 K 声明为 comparable 是必要的，以便您可以使用 K 作为映射变量中的键。它还确保调用代码对映射键使用允许的类型 指定 m 参数的类型为 map[K]V 类型，其中 K 和 V 是已为类型参数指定的类型。请注意，我们知道 map[K]V 是有效的映射类型，因为 K 是可比较的类型。如果我们没有声明 K 具有可比性，编译器将拒绝对 map[K]V 的引用\nSumIntsOrFloats[string, int64](ints) 简化为 SumIntsOrFloats(ints)\n断言 将接口与结构体连接并赋予类型，再判断是否正确\n1 2 3 4 5 6 7 var all allType all = new(aaa) all = \u0026#34;abc\u0026#34; str, st := all.(string) fmt.Println(str) fmt.Println(st) fmt.Println(reflect.TypeOf(str)) interface 1 2 3 4 5 6 7 8 9 10 11 12 13 type Phone interface { call() } type NokiaPhone struct { } func (no NokiaPhone) call() { fmt.Println(\u0026#34;calling\u0026#34;) } var phone Phone phone = new(NokiaPhone) phone.call() 如果没有将 interface 给 new 成 struct 则会报错\n1 2 3 4 5 6 7 8 PS C:\\Users\\xx\\gotree\u0026gt; go run .\\main.go panic: runtime error: invalid memory address or nil pointer dereference [signal 0xc0000005 code=0x0 addr=0x0 pc=0xb66670] goroutine 1 [running]: main.main() C:/Users/xx/gotree/main.go:64 +0x10 exit status 2 new 和 make 的区别 new 分配内存,只有一个参数,int， 数组，结构体，甚至函数类型都可以是 new 的参数，返回的是指针 make 分配和初始化内存,只能用于 slice, map 和 chan 这 3 个类型,返回的是原始类型\nmap 如果是 nil，是不能往 map 插入元素的，插入元素会引发 panic chan 如果是 nil，往 chan 发送数据或者从 chan 接收数据都会阻塞 slice 会有点特殊，理论上 slice 如果是 nil，也是没法用的。但是 append 函数处理了 nil slice 的情况，可以调用 append 函数对 nil slice 做扩容。但是我们使用 slice，总是会希望可以自定义长度或者容量，这个时候就需要用到 make。\nmake 可以开辟新空间，规定 len 和 cap new 虽然也可以初始化，但只能得到 nil\ngo 数组与切片 数组一旦定义长度无法改变长度,所以一般不定义长度使用切片 切片添加元素时，\u0026quot;\u0026hellip;\u0026ldquo;代表添加的不止一个元素 二维数组添加元素\n1 2 3 4 nums0 := [][]int{{1, 2}, {3, 4}} fmt.Println(nums0) nums0 = append(nums0, []int{5, 6}) fmt.Println(nums0) 不改变原来数组的内存 ans = append(ans, append([]int(nil), nums...))（正确写法） 需要注意的是，为了避免对原切片造成影响，代码在将当前排列追加到结果数组 ans 时，使用了 append(ans, append([]int(nil), nums…)) 的方式创建了一个新的切片，保证了 nums 切片和新切片不共享内存\n小数点处理 github.com/shopspring/decimal fmt.Println(decimal.NewFromFloat(5.0 / 2.0).Round(0).Float64())\n切片 make(类型，长度，容量) var 声明全局，:=声明局部\n字符串注意事项 单引号用来表示单个字符串，c 语言中的 char\n双引号代表字符串\n反引号忽略转义字符\nrune 用来转化为 int32 位，表示 ascii 码\nbyte 代表 UTF-8 字符串的 1 个字节的值，表示一个 ASCII 码字符,utf-8 中一个中文占三个字节 rune（实际上是 int32），代表单个 Unicode 字符 4 个字节，常用来处理 unicode 或 utf-8 字符（一切字符） Go 语言的 string 是用 uft-8 进行编码的，英文字母占用一个字节，而中文字母占用 3 个字节 %q 解释型字符串，反引号可以不写换行符（因为没法写）来表示一个多行的字符串\n字符格式化输出 fmt.Print 小数点和替换 保留两位小数 strconv.Sprintf value, _ := strconv.ParseFloat(fmt.Sprintf(\u0026quot;%.2f\u0026quot;, 9.824), 64) fmt.Println(value) //9.82\n\u0026ldquo;github.com/shopspring/decimal\u0026rdquo; v1, _ := decimal.NewFromFloat(9.824).Round(2).Float64()\nStrings 包 func Repeat(s string, k int) string func Replace(s, old, new string, n int) string\n","date":"2023-05-07T00:00:00Z","permalink":"https://cancanneed64.github.io/p/go%E6%95%B0%E6%8D%AE%E5%A4%84%E7%90%86/","title":"Go数据处理"},{"content":"vscode 多行处理快捷键\n使用 git\n工作区对比 连接\nmarkdown 插件 安装 Markdown All in One 实时预览 创建 TOC \u0026gt; Create Table of Contents 快捷键 ctrl+b/i alt+s Markdown PDF 导出为 PDF \u0026gt; export pdf markdownlint 规范书写格式 ","date":"2023-05-07T00:00:00Z","permalink":"https://cancanneed64.github.io/p/vscode%E6%8A%80%E5%B7%A7/","title":"vscode技巧"},{"content":"excel 强制 xls 保存解决方法 原因 一些 excel 文件带着名为 Kangatang.bas 的恶意模块，用户只要同时打开其他 excel 文件便会\u0026quot;传染\u0026quot;上，同时在C:\\Users\\id\\AppData\\Roaming\\Microsoft\\Excel路径 xlstart 留下一个 mypersonnel 文件，并且用户会发现再也无法保存文件为 xlsx，只能保存为 xls 格式\nKangatang.bas 展开代码 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 32 33 34 35 36 37 38 Attribute VB_Name = \u0026#34;Kangatang\u0026#34; Sub Auto_Open() \u0026#39;If ThisWorkbook.Path \u0026lt;\u0026gt; Application.Path \u0026amp; \u0026#34;\\XLSTART\u0026#34; Then ThisWorkbook.SaveAs Filename:=Application.Path \u0026amp; \u0026#34;\\XLSTART\\mypersonel.xls\u0026#34; Application.DisplayAlerts = False On Error Resume Next If ThisWorkbook.Path \u0026lt;\u0026gt; Application.StartupPath Then Application.ScreenUpdating = False Windows(1).Visible = False ThisWorkbook.SaveCopyAs Filename:=Application.StartupPath \u0026amp; \u0026#34;\\mypersonnel.xls\u0026#34; Windows(1).Visible = True End If Application.OnSheetActivate = \u0026#34;\u0026#34; Application.ScreenUpdating = True Application.OnSheetActivate = \u0026#34;mypersonnel.xls!allocated\u0026#34; End Sub Sub Auto_Close() On Error Resume Next Application.DisplayAlerts = False If Right(ThisWorkbook.Name, 4) \u0026lt;\u0026gt; \u0026#34;xlsx\u0026#34; Or Application.Version \u0026lt;= 11 Then Exit Sub ThisWorkbook.SaveAs Filename:=ThisWorkbook.Path \u0026amp; \u0026#34;\\\u0026#34; \u0026amp; Replace(ThisWorkbook.Name, \u0026#34;.xlsx\u0026#34;, \u0026#34;.xls\u0026#34;), _ FileFormat:=xlExcel8, Password:=\u0026#34;\u0026#34;, WriteResPassword:=\u0026#34;\u0026#34;, _ ReadOnlyRecommended:=False, CreateBackup:=False Kill ThisWorkbook.Path \u0026amp; \u0026#34;\\\u0026#34; \u0026amp; Replace(ThisWorkbook.Name, \u0026#34;.xls\u0026#34;, \u0026#34;.xlsx\u0026#34;) End Sub Sub allocated() On Error Resume Next If ActiveWorkbook.Sheets(1).Name \u0026lt;\u0026gt; \u0026#34;Kangatang\u0026#34; Then Application.ScreenUpdating = False currentsh = ActiveSheet.Name ThisWorkbook.Sheets(\u0026#34;Kangatang\u0026#34;).Copy before:=ActiveWorkbook.Sheets(1) ActiveWorkbook.Sheets(currentsh).Select Application.ScreenUpdating = True End If End Sub 一般解决流程 开启 excel 宏功能（已开启可以跳过到 2）\n文件 – 选项 – Excel 选项 – 自定义功能区 – 勾选开发工具\n删除 excel 模块并保存关闭（记得先删除保存后关闭，否则会占用 3 中的文件）\n打开有问题的 excel，点击开发工具，查看代码，单选模块，右键移除，选择不导出，保存关闭文件\n删除个人账户 AppData 文件（如果没有删除 2 中的模块重新打开文件，会再次生成 mypersonnel）\n之后再下面的目录 C:\\Users\\id\\AppData\\Roaming\\Microsoft\\Excel 目录下把 xlstart 的 mypersonnel 删除\nPython 制作便捷小工具 Github 下载地址\n使用说明\n先创建一个新的文件夹 将“程序”与问题 excel 单个文件一同放到新的文件夹中 运行“程序” “程序”将会单独删除 excel 中的问题模块保存关闭 注意需要创建新的文件夹，一次只能删除一个 excel 文件的模块，避免目录下文件数量过多，否则会运行失败\n展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 import win32com.client import os import win32api, win32con def find_file(l): path = os.getcwd() #print(path) Files = os.listdir(path) for k in range(len(Files)): if Files[k].endswith(\u0026#39;xlsx\u0026#39;) or Files[k].endswith(\u0026#39;xls\u0026#39;) or Files[k].endswith(\u0026#39;xlsm\u0026#39;): l.append(os.path.join(path, Files[k])) else: continue return l def delete_module(xlwb): for i in xlwb.VBProject.VBComponents: xlmodule = xlwb.VBProject.VBComponents(i.Name) if xlmodule.Type in [1, 2, 3]: if i.Name == \u0026#39;Kangatang\u0026#39;: xlwb.VBProject.VBComponents.Remove(xlmodule) n = n +1 #print(\u0026#34;remove xlmodule\u0026#34;) if os.path.exists(exist_file): os.remove(exist_file) #print(\u0026#34;remove excelfile\u0026#34;) exist_file = \u0026#39;C:\\\\Users\\\\{0}\\\\AppData\\\\Roaming\\\\Microsoft\\\\Excel\\\\XLSTART\\\\mypersonnel.xls\u0026#39;.format(os.getlogin()) l = [] pathlis = find_file(l) global n n = 0 xlApp = win32com.client.Dispatch(\u0026#34;Excel.Application\u0026#34;) xlwb = None #创建路径下文件名 #遍历path列表，1空列表提示，2excel文件加到列表 #print(path) # ITERATE THROUGH EACH VB COMPONENT (CLASS MODULE, STANDARD MODULE, USER FORMS) try: print(pathlis) if pathlis == []: win32api.MessageBox(0 ,\u0026#34;请将文件放到有excel的目录里\u0026#34;,\u0026#34;消息框\u0026#34;,win32con.MB_OK) # OPEN EXCEL APP AND WORKBOOK #win32api.MessageBox(0 ,\u0026#34;当前路径为\u0026#34;+path,\u0026#34;消息框\u0026#34;,win32con.MB_OK) else: for path in range(len(pathlis)): print(path) xlwb = xlApp.Workbooks.Open(pathlis[path]) delete_module(xlwb) xlwb.Close(True) if n != 0: win32api.MessageBox(0,\u0026#34;共删除模块{0}次\u0026#34;.format(n),\u0026#34;消息框\u0026#34;, win32con.MB_OK) else: win32api.MessageBox(0,\u0026#34;没有发现问题文件\u0026#34;,\u0026#34;消息框\u0026#34;, win32con.MB_OK) except Exception as e: print(e) #win32api.MessageBox(0, e ,\u0026#34;消息框\u0026#34;, win32con.MB_OK) finally: # CLOSE AND SAVE AND UNINITIALIZE APP xlApp.Quit xlApp = None ","date":"2023-04-25T00:00:00Z","permalink":"https://cancanneed64.github.io/p/excel%E5%BC%BA%E5%88%B6xls%E4%BF%9D%E5%AD%98%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95/","title":"excel强制xls保存解决方法"},{"content":"Python 爬取统计页面分析 需求: 实现 selenium 登录爬取页面数据，提取整合需要的内容\n分析: 在手动实现登录页面后，通过 Network 查看返回数据，找到有可能获取的数据连接，发起构造 url 请求 原本链接为nav_to.do?uri=/home.do?，而筛选后发现 url 变化sysparm_query%3Dactive%253Dtrue%255Eassignment_group%253Djavascript:getMyGroups()%255Estate%2521%253D6%255EEQ 可依次根据关键字进行构造，从而添加上日期和页面数修改为 /nav_to.do?uri=%2Fincident_list.do%3Fsysparm_query%3Dassignment_group%253Djavascript:getMyGroups()%255Esys_created_on%253E%253Djavascript:gs.dateGenerate(%25272022-01-01%2527%252C%252700:00:00%2527)%26sysparm_first_row%3D101%26sysparm_view%3D_stack%3Dtrue%26sysparm_userpref_module%3D70782f04db8ab20099f47bedae961972%26sysparm_query%3Dactive%253Dtrue%255Eassignment_group%253Djavascript:getMyGroups()%255Estate%2521%253D6%255EEQ\n尝试手动登录并返回页面数据 用 selenium 登录网页，在创建一个 session 保持登陆的 cookie，构造 header 进行 request 请求\n展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 # coding:utf-8 # 用webdriver登录并获取cookies，并用requests发送请求，以豆瓣为例 from selenium import webdriver import requests import time import json import sys def main(): # 从命令行参数获取登录用户名和密码 user_name = \u0026#39;\u0026#39; password = \u0026#39;\u0026#39; # 登录页面URL login_url = \u0026#39;/nav_to.do?uri=%2Fincident_list.do%3Fsysparm_query%3Dassignment_group%253Djavascript:getMyGroups()%255Esys_created_on%253E%253Djavascript:gs.dateGenerate(%25272022-01-01%2527%252C%252700:00:00%2527)%26sysparm_first_row%3D1%26sysparm_view%3D_stack%3Dtrue%26sysparm_userpref_module%3D70782f04db8ab20099f47bedae961972%26sysparm_query%3Dactive%253Dtrue%255Eassignment_group%253Djavascript:getMyGroups()%255Estate%2521%253D6%255EEQ\u0026#39; # 获取chrome的配置 opt = webdriver.ChromeOptions() # 在运行的时候不弹出浏览器窗口 # opt.set_headless() # 获取driver对象 driver = webdriver.Chrome(chrome_options = opt) # 打开登录页面 driver.get(login_url) print(\u0026#39;opened login page...\u0026#39;) # 向浏览器发送用户名、密码，并点击登录按钮 #driver.find_element_by_css_selector(\u0026#39;\u0026#39;).click() time.sleep(2) driver.find_element_by_css_selector(\u0026#39;#username\u0026#39;).send_keys(user_name) driver.find_element_by_css_selector(\u0026#39;#password\u0026#39;).send_keys(password) #time.sleep(6) #driver.find_element_by_css_selector(\u0026#39;#login \u0026gt; button\u0026#39;).click() # 多次登录需要输入验证码，这里给一个手工输入验证码的时间 time.sleep(6) print(\u0026#39;submited...\u0026#39;) # 等待2秒钟 time.sleep(20) # 创建一个requests session对象 s = requests.Session() # 从driver中获取cookie列表（是一个列表，列表的每个元素都是一个字典） cookies = driver.get_cookies() # 把cookies设置到session中 for cookie in cookies: s.cookies.set(cookie[\u0026#39;name\u0026#39;],cookie[\u0026#39;value\u0026#39;]) headers = { \u0026#34;User-Agent\u0026#34;:\u0026#39;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36\u0026#39; } page = 1 # 需要登录才能看到的页面URL for i in range(1,912,100): page_url = \u0026#39;/incident_list.do?sysparm_query=assignment_group%3Djavascript:getMyGroups()%5Esys_created_on%3E%3Djavascript:gs.dateGenerate(%272022-01-01%27%2C%2700:00:00%27)\u0026amp;sysparm_first_row={}\u0026amp;sysparm_view=\u0026#39;.format(i) # 获取该页面的HTML resp = s.get(page_url,headers=headers) resp.encoding = \u0026#39;utf-8\u0026#39; print(\u0026#39;status_code = {0}\u0026#39;.format(resp.status_code)) # 将网页内容存入文件 with open(\u0026#39;{}page.txt\u0026#39;.format(i),\u0026#39;w+\u0026#39;,encoding=\u0026#39;utf-8\u0026#39;) as fout: fout.write(resp.text) print(\u0026#39;现在已经进行到第{}页\u0026#39;.format(page)) page = page + 1 print(\u0026#39;end\u0026#39;) if __name__ == \u0026#39;__main__\u0026#39;: main() 将获取返回数据进行清洗 用 xpath 对返回的请求进行元素匹配，再用正则提取需要的格式，最终存到 csv 中 展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 import time import json import sys from lxml import etree import re import pandas as pd #读取txt数据,转化成lxml模式并打印 def main(): linc,lopenby,ldate,lstate,lassignto,lmar,lshortdes,ldes = [[] for i in range(8)] lis = [] n = 0 f = open(\u0026#39;1page.txt\u0026#39;,\u0026#39;r\u0026#39;,encoding=\u0026#39;utf-8\u0026#39;) st = f.read() html = etree.HTML(st) item = html.xpath(\u0026#39;//*[@id=\u0026#34;incident_table\u0026#34;]/tbody\u0026#39;) inc = item[0].xpath(\u0026#39;.//tr/td[3]/a/text()\u0026#39;) #第二列 openby = item[0].xpath(\u0026#39;.//tr/td[5]/a/text()\u0026#39;) #第四列 date = item[0].xpath(\u0026#39;.//tr/td[6]/div[1]/text()\u0026#39;) #第五列 state = item[0].xpath(\u0026#39;.//tr/td[7]/text()\u0026#39;) #第六列 assignto = item[0].xpath(\u0026#39;.//tr/td[9]/a/text()\u0026#39;) #第八列 mar = item[0].xpath(\u0026#39;.//tr/td[11]/text()\u0026#39;) #第十列 shortdes = item[0].xpath(\u0026#39;.//tr/td[14]/text()\u0026#39;) #第十三列 #des = item[0].xpath(\u0026#39;.//tr/td[15]/@title\u0026#39;) #第十四列 des = item[0].xpath(\u0026#39;.//tr/td[15]/text()\u0026#39;) #第十四列 print(len(inc),len(des)) with open(\u0026#39;desscan.txt\u0026#39;,\u0026#39;a\u0026#39;,encoding=\u0026#39;utf-8\u0026#39;) as f: for i in des: f.write(i+\u0026#39;\\n\u0026#39;+\u0026#39;\\n\u0026#39;) ## print(len(inc),len(openby),len(date),len(state),len(assignto),len(mar),len(shortdes),len(des)) ## ## for i in range(len(inc)-1): ## temp = inc[i] + openby[i] + date[i] + state[i] + assignto[i] + mar[i] + shortdes[i] + des[i] ## #lis.append(inc[i],openby[i],date[i],state[i],assignto[i],mar[i],shortdes[i],des[i]) ## lis.append(temp) ## print(i) ## print(lis[0]) if __name__ == \u0026#39;__main__\u0026#39;: main() 展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 import time import json import sys from lxml import etree import re import pandas as pd import numpy as np def main(): titlelis = [\u0026#39;inc\u0026#39;,\u0026#39;openby\u0026#39;,\u0026#39;date\u0026#39;,\u0026#39;state\u0026#39;,\u0026#39;assignto\u0026#39;,\u0026#39;mar\u0026#39;,\u0026#39;shortdes\u0026#39;,\u0026#39;des\u0026#39;] for num in range(10): lis = [[] for i in range(100)] f = open(\u0026#39;{}01page.txt\u0026#39;.format(num),\u0026#39;r\u0026#39;,encoding=\u0026#39;utf-8\u0026#39;) st = f.read() print(\u0026#39;open {}01page.txt\u0026#39;.format(num)) html = etree.HTML(st) ##result = etree.tostring(html).decode() #验证 item = html.xpath(\u0026#39;//*[@id=\u0026#34;incident_table\u0026#34;]/tbody\u0026#39;) #page = re.findall(r\u0026#39;http://gd.zgsydw.com/ziliao/shizheng/(.*?).html\u0026#39;,url) #预留 #with open(\u0026#39;lis.txt\u0026#39;,\u0026#39;a+\u0026#39;,encoding=\u0026#39;utf-8\u0026#39;) as f: for i in range(1,101): inc = item[0].xpath(\u0026#39;.//tr[{}]/td[3]/a/text()\u0026#39;.format(i)) #第二列 openby = item[0].xpath(\u0026#39;.//tr[{}]/td[5]/a/text()\u0026#39;.format(i)) #第四列 date = item[0].xpath(\u0026#39;.//tr[{}]/td[6]/div[1]/text()\u0026#39;.format(i)) #第五列 state = item[0].xpath(\u0026#39;.//tr[{}]/td[7]/text()\u0026#39;.format(i)) #第六列 assignto = item[0].xpath(\u0026#39;.//tr[{}]/td[9]/a/text()\u0026#39;.format(i)) #第八列 mar = item[0].xpath(\u0026#39;.//tr[{}]/td[11]/text()\u0026#39;.format(i)) #第十列 shortdes = item[0].xpath(\u0026#39;.//tr[{}]/td[14]/text()\u0026#39;.format(i)) #第十三列 des = item[0].xpath(\u0026#39;.//tr[{}]/td[15]/@title\u0026#39;.format(i)) #第十四列 if des == []: des = item[0].xpath(\u0026#39;.//tr[{}]/td[15]/text()\u0026#39;.format(i)) #第十四列 lis[i - 1].append(\u0026#34;\u0026#34;.join(inc)) lis[i - 1].append(\u0026#34;\u0026#34;.join(openby)) lis[i - 1].append(\u0026#34;\u0026#34;.join(date)) lis[i - 1].append(\u0026#34;\u0026#34;.join(state)) lis[i - 1].append(\u0026#34;\u0026#34;.join(assignto)) lis[i - 1].append(\u0026#34;\u0026#34;.join(mar)) lis[i - 1].append(\u0026#34;\u0026#34;.join(shortdes)) lis[i - 1].append(\u0026#34;\u0026#34;.join(des)) print(lis) #lis不断累加前次结果 #inc,openby,date,state,assignto,mar,shortdes,des #预留 #将全部写入txt #f.write(\u0026#34;\u0026#34;.join(lis[i-1][j][j])+\u0026#39;\\n\u0026#39;) #f.write(\u0026#39;\\n\u0026#39;) indexlis = [num * 100 + i for i in range(1,101)] if num == 9: #print(lis) lis = [\u0026#34;\u0026#34;.join(list(filter(None,\u0026#34;\u0026#34;.join(lis))))] #print(lis) #indexlis = [num * 100 + i for i in range(1,len(lis))] #print(len(lis),len(indexlis),indexlis) a = pd.DataFrame(lis,columns=titlelis,index=indexlis) a.to_csv(\u0026#39;panlis.csv\u0026#39;, encoding=\u0026#39;utf-8-sig\u0026#39;,mode=\u0026#39;a+\u0026#39;) if __name__ == \u0026#39;__main__\u0026#39;: main() 序列化并提取词汇 转化为 pandas 格式，尝试用 nltk 将高频词汇提取出来用 jieba 制作词云，但当时免费库还不够成熟，效果不理想\n展开代码 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 32 33 34 35 36 37 38 39 40 41 import pandas as pd import time import numpy as np import nltk from nltk.stem.porter import PorterStemmer from nltk.stem.lancaster import LancasterStemmer from nltk.stem import SnowballStemmer def main(): df = pd.read_csv(\u0026#39;panlis.csv\u0026#39;) df2 =df[\u0026#39;des\u0026#39;] #print(df2) ar = np.array(df2) lis = ar.tolist() text = \u0026#34;\u0026#34;.join(lis).lower() #print(text) words = nltk.word_tokenize(text) #lancaster lancaster_stemmer = LancasterStemmer() print(lancaster_stemmer.stem(\u0026#39;maximum\u0026#39;)) print(lancaster_stemmer.stem(\u0026#39;multiply\u0026#39;)) #porter porter_stemmer = PorterStemmer() print(porter_stemmer.stem(\u0026#39;maximum\u0026#39;)) print(porter_stemmer.stem(\u0026#39;multiply\u0026#39;)) #snowball snowball_stemmer = SnowballStemmer(\u0026#39;english\u0026#39;) print(snowball_stemmer.stem(\u0026#39;maximum\u0026#39;)) print(snowball_stemmer.stem(\u0026#39;multiply\u0026#39;)) text2 = nltk.word_tokenize(text) print(text2) print(nltk.pos_tag(text2)) if __name__ == \u0026#39;__main__\u0026#39;: main() ","date":"2022-06-13T00:00:00Z","permalink":"https://cancanneed64.github.io/p/python%E7%88%AC%E5%8F%96%E7%BB%9F%E8%AE%A1%E9%A1%B5%E9%9D%A2%E5%88%86%E6%9E%90/","title":"Python爬取统计页面分析"},{"content":"Python 办公小工具 Github 下载地址\n根据图片拖动目标 一天同事突然问到有没有办法在页面中一次性批量选中所有文件拖动进一个文件夹，碰巧页面没有开发这类全选的功能，只能一个个处理，于是尝试做了一下这个拖动小工具\n前文 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 1.安装python3.4以上版本，并配置环境变量 教程：https://www.runoob.com/python3/python3-install.html 2.安装依赖包 方法：在cmd中（win+R 输入cmd 回车）输入 pip install pyperclip 回车 pip install xlrd 回车 pip install pyautogui==0.9.50 回车 pip install opencv-python 回车 pip install pillow 回车 3.把每一步要操作的图标、区域截图保存至本文件夹 png格式（注意如果同屏有多个相同图标，回默认找到最左上的一个，因此怎么截图，截多大的区域，是个学问，如输入框只截中间空白部分肯定是不行的，宗旨就是“唯一”） 4.在cmd.xls 的sheet1 中，配置每一步的指令，如指令类型1234 对应的内容填截图文件名，指令5对应的内容是等待时长（单位秒） 指令6对应的内容是滚轮滚动的距离，正数表示向上滚，负数表示向下滚 5.保存文件 6.双击waterRPA.py打开程序，按1表示excel中的指令执行一次，按2表示无限重复执行直到程序关闭 7.开始程序后请将程序框最小化，不然程序框挡住的区域是无法识别和操作的 8.如果程序开始后因为你选择了无限重复而鼠标被占用停不下来，alt+F4吧~ 想自己开发和优化的 可以看看pyautogui库其他用法 https://blog.csdn.net/qingfengxd1/article/details/108270159 二次开发 流程:\n根据\u0026quot;cmd.xls\u0026quot;作为数据库的思想，读取行数与内容 第一格输入数字代表模式，1 单击 2 双击 3 右键 4 输入 5 等待 6 滚轮 7 拖拽，第二格需要把从 A 拖曳到 B 的两个位置图标图片保存进文件夹中，并将名称保存到表格，第三格表示重复次数(-1 代表一直重复) 后台一直监听鼠标，当鼠标移到屏幕的左上角抛出 failSafeException 异常结束循环 展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 import pyautogui import time import xlrd import pyperclip pyautogui.FAILSAFE = True # 启用自动防故障功能，左上角的坐标为（0，0），将鼠标移到屏幕的左上角，来抛出failSafeException异常 def mouseClick(clickTimes,lOrR,img,reTry): #点击时间，左右键，图片名，重复次数 if reTry == 1: while True: location = pyautogui.locateCenterOnScreen(img,confidence=0.9) if location is not None: pyautogui.click(location.x,location.y,clicks=clickTimes,interval=0.2,duration=0.2,button=lOrR) break print(\u0026#34;未找到匹配图片,0.1秒后重试\u0026#34;) time.sleep(0.1) elif reTry == -1: while True: print(\u0026#39;循环\u0026#39;) location=pyautogui.locateCenterOnScreen(img,confidence=0.9) print(location) if location is not None: pyautogui.click(location.x,location.y,clicks=clickTimes,interval=0.2,duration=0.2,button=lOrR) time.sleep(0.1) elif reTry \u0026gt; 1: i = 1 while i \u0026lt; reTry + 1: location=pyautogui.locateCenterOnScreen(img,confidence=0.9) if location is not None: pyautogui.click(location.x,location.y,clicks=clickTimes,interval=0.2,duration=0.2,button=lOrR) print(\u0026#34;重复\u0026#34;) i += 1 time.sleep(0.1) def mainWork(img): i = 1 while i \u0026lt; sheet1.nrows: #如果i小于总行数 cmdType = sheet1.row(i)[0] if cmdType.value == 7.0: reTry = 1 imglis = (sheet1.row(i)[1].value).split(\u0026#34;,\u0026#34;) img = imglis[0] img2 = imglis[1] #reTry为空单次 location = pyautogui.locateCenterOnScreen(img,confidence=0.9) #定位起始位置 print(location) pyautogui.moveTo(location.x,location.y) #移动到图片坐标 destination = pyautogui.locateCenterOnScreen(img2,confidence=0.9) #目标图片图片 print(destination) if location is not None: pyautogui.dragTo(destination.x,destination.y, button=\u0026#39;left\u0026#39;,duration=0.5) #拖拽到目标位置 print(\u0026#39;拖拽\u0026#39;,img+\u0026#34;到\u0026#34;+img2) #reTry不为空多次 if sheet1.row(i)[2].ctype == 2 and sheet1.row(i)[2].value != 0: #第3列第2行非空 #ctype : 0 empty,1 string, 2 number, 3 date, 4 boolean, 5 error reTry = sheet1.row(i)[2].value #如果非空则赋值，回到循环小于等于i则退出循环 for r in range(int(reTry),-1,-1): location = pyautogui.locateCenterOnScreen(img,confidence=0.9) #定位起始位置 print(location) pyautogui.moveTo(location.x,location.y) #移动到图片坐标 destination = pyautogui.locateCenterOnScreen(img2,confidence=0.9) #目标图片图片 print(destination) if location is not None: pyautogui.dragTo(destination.x,destination.y, button=\u0026#39;left\u0026#39;,duration=0.5) #拖拽到目标位置 print(\u0026#39;拖拽\u0026#39;,img+\u0026#34;到\u0026#34;+img2) time.sleep(1) i+=1 if __name__ == \u0026#39;__main__\u0026#39;: file = \u0026#39;cmd.xls\u0026#39; #打开文件 wb = xlrd.open_workbook(filename=file) #通过索引获取表格sheet页 sheet1 = wb.sheet_by_index(0) mainWork(sheet1) 打开网页点击目标 跟平时调用的 selenium 不同，尝试了下 pyppeteer 需求: 打开网页实现自动点击\n展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 import asyncio, time from pyppeteer import launch from win10toast import ToastNotifier async def main(mod,webpage): browser = await launch(headless=mod, dumpio=True, autoClose=False, args=[\u0026#39;--no-sandbox\u0026#39;, \u0026#39;--window-size=1920,1080\u0026#39;, \u0026#39;--disable-infobars\u0026#39;]) # 进入有头模式 page_list = await browser.pages() #page = await browser.newPage() # 打开新的标签页 page = page_list[-1] await page.setViewport({\u0026#39;width\u0026#39;: 1920, \u0026#39;height\u0026#39;: 1080}) # 页面大小一致 await page.goto(webpage) # 访问主页 # evaluate()是执行js的方法，js逆向时如果需要在浏览器环境下执行js代码的话可以利用这个方法 # js为设置webdriver的值，防止网站检测 await page.evaluate(\u0026#39;\u0026#39;\u0026#39;() =\u0026gt;{ Object.defineProperties(navigator,{ webdriver:{ get: () =\u0026gt; false } }) }\u0026#39;\u0026#39;\u0026#39;) # await page.screenshot({\u0026#39;path\u0026#39;: \u0026#39;./1.jpg\u0026#39;}) # 截图保存路径 #page_text = await page.content() # 获取网页源码 #print(page_text) #time.sleep(1) # 输入要查询的关键字，type第一个参数是元素的selector，第二个是要输入的关键字 #await page.type(\u0026#39;#kw\u0026#39;, \u0026#39;pyppeteer\u0026#39;) # 点击提交按钮 click通过selector点击指定的元素 ## await page.click(\u0026#39;#notify-content \u0026gt; form \u0026gt; div:nth-child(4) \u0026gt; div \u0026gt; input.btn.btn-success\u0026#39;, ## options={\u0026#39;button\u0026#39;: \u0026#39;left\u0026#39;, #left, right, of middle, defaults to left ## \u0026#39;clickCount\u0026#39;: 1, # 1 or 2 ## \u0026#39;delay\u0026#39;: 300, # 毫秒 ## }) #等待元素出现 #WR = await page.waitForSelector(\u0026#39;#notify-content \u0026gt; form \u0026gt; div:nth-child(4) \u0026gt; div \u0026gt; input.btn.btn-success\u0026#39;) while True: try: await page.click(\u0026#39;#notify-content \u0026gt; form \u0026gt; div:nth-child(4) \u0026gt; div \u0026gt; input.btn.btn-success\u0026#39;) print(\u0026#34;have a page elem\u0026#34;) print(\u0026#34;click complete,waiting for next time\u0026#34;) except: print(\u0026#34;waiting\u0026#34;) await asyncio.sleep(60) await page.reload() #await browser.close() #toaster = ToastNotifier() #toaster.show_toast(\u0026#34;Tip\u0026#34;,\u0026#39;Complete Click github!!!\u0026#39;, threaded=False, icon_path=None, duration=100) mod = False webpage = \u0026#39;https://github.com/\u0026#39; asyncio.get_event_loop().run_until_complete(main(mod,webpage)) #调用 1 2 python D:\\pdemo\\peteer\\clickgithub.py quit() 读取文本并运行保存 os.popen 可以直接 cmd 运行文本命令，尝试着用 csv 传递域名进行 ping 并保存结果\n展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 import os import time import re import pandas as pd import numpy as np #读取csv文件转化为列表 def read_info(path): df = pd.read_csv(path) #ar = np.array(df) #lis = ar.tolist() lis= df.values.tolist() text = [\u0026#34;\u0026#34;.join(l) for l in lis ] #print(text) return text #取str符号\u0026#34;.\u0026#34;中间段得字符座位文件名称保存 def find_filename(cmd,i): start = i.find(\u0026#34;.\u0026#34;,0) + 1 end = i.find(\u0026#34;.\u0026#34;,start) name = cmd + i[start:end] return name #正则查找字符串作为文件名称保存 def re_filename(cmd,i): pattern = re.compile(r\u0026#39;(?\u0026lt;=\\.)(.*)(?=\\.)\u0026#39;) name = cmd + pattern.findall(i) return name #保存文件名称 def save_txt(info,name): with open(\u0026#39;{}.txt\u0026#39;.format(name),\u0026#39;w+\u0026#39;)as f: f.write(info) f.close() if __name__ == \u0026#39;__main__\u0026#39;: weblis = [\u0026#34;outlook.office365.com\u0026#34;, \u0026#34;teams.microsoft.com\u0026#34;] web = [\u0026#34;outlook.office365.com\u0026#34;] path = \u0026#39;1.csv\u0026#39; text = read_info(path) cmd = \u0026#39;ping \u0026#39; for i in text: p = os.popen(cmd + i) print(p.read()) name = find_filename(cmd,i) print(name) save_txt(p.read(),name) \u0026#39;\u0026#39;\u0026#39; \u0026gt;\u0026gt;\u0026gt; help(a[0].index) Help on built-in function index: index(...) method of builtins.str instance S.index(sub[, start[, end]]) -\u0026gt; int Return the lowest index in S where substring sub is found, such that sub is contained within S[start:end]. Optional arguments start and end are interpreted as in slice notation. Raises ValueError when the substring is not found. \u0026#39;\u0026#39;\u0026#39; 模拟 QQ 截图模块 流程: 根据鼠标的两个点坐标进行获取图片并保存\n展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 import cv2 import time,PIL,os from PIL import ImageGrab import numpy as np #截图 def cut(): global img scrren_cut() img = cv2.imread(\u0026#39;screen.jpg\u0026#39;) cv2.namedWindow(\u0026#39;image\u0026#39;) cv2.setMouseCallback(\u0026#39;image\u0026#39;, on_mouse) cv2.imshow(\u0026#39;image\u0026#39;, img) cv2.waitKey(0) os.remove(\u0026#39;screen.jpg\u0026#39;) def scrren_cut(): beg = time.time() debug = False # img = ImageGrab.grab(bbox=(250, 161, 1141, 610)) image = ImageGrab.grab() image.save(\u0026#34;screen.jpg\u0026#34;) # PIL image to OpenCV image def on_mouse(event, x, y, flags, param): global img, point1, point2 img2 = img.copy() if event == cv2.EVENT_LBUTTONDOWN: #左键点击 point1 = (x,y) cv2.circle(img2, point1, 10, (0,255,0), 5) cv2.imshow(\u0026#39;image\u0026#39;, img2) elif event == cv2.EVENT_MOUSEMOVE and (flags \u0026amp; cv2.EVENT_FLAG_LBUTTON): #按住左键拖曳 cv2.rectangle(img2, point1, (x,y), (255,0,0), 5) cv2.imshow(\u0026#39;image\u0026#39;, img2) elif event == cv2.EVENT_LBUTTONUP: #左键释放 point2 = (x,y) cv2.rectangle(img2, point1, point2, (0,0,255), 5) cv2.imshow(\u0026#39;image\u0026#39;, img2) min_x = min(point1[0],point2[0]) min_y = min(point1[1],point2[1]) width = abs(point1[0] - point2[0]) height = abs(point1[1] -point2[1]) cut_img = img[min_y:min_y+height, min_x:min_x+width] cv2.imwrite(\u0026#39;cut.jpg\u0026#39;, cut_img) if __name__ == \u0026#39;main\u0026#39; : cut() 遍历解压文件夹 流程: 遍历文件夹并解压文件\n展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 import os import time import shutil \u0026#39;\u0026#39;\u0026#39; import rarfile import zipfile import tarfile import gzip def un_rar(file_name): \u0026#34;\u0026#34;\u0026#34;unrar zip file\u0026#34;\u0026#34;\u0026#34; rar = rarfile.RarFile(file_name) if os.path.isdir(file_name + \u0026#34;_files\u0026#34;): pass else: os.mkdir(file_name + \u0026#34;_files\u0026#34;) if os.chdir(file_name + \u0026#34;_files\u0026#34;): rar.extractall() rar.close() def un_zip(file_name): \u0026#34;\u0026#34;\u0026#34;unzip zip file\u0026#34;\u0026#34;\u0026#34; zip_file = zipfile.ZipFile(file_name) if os.path.isdir(file_name + \u0026#34;_files\u0026#34;): pass else: os.mkdir(file_name + \u0026#34;_files\u0026#34;) for names in zip_file.namelist(): zip_file.extract(names,file_name + \u0026#34;_files/\u0026#34;) zip_file.close() def un_tar(file_name): tar = tarfile.open(file_name) names = tar.getnames() if os.path.isdir(file_name + \u0026#34;_files\u0026#34;): pass else: os.mkdir(file_name + \u0026#34;_files\u0026#34;) #因为解压后是很多文件，预先建立同名目录 for name in names: tar.extract(name, file_name + \u0026#34;_files/\u0026#34;) tar.close() def un_gz(file_name): \u0026#34;\u0026#34;\u0026#34;ungz zip file\u0026#34;\u0026#34;\u0026#34; f_name = file_name.replace(\u0026#34;.gz\u0026#34;, \u0026#34;\u0026#34;) #获取文件的名称，去掉 g_file = gzip.GzipFile(file_name) #创建gzip对象 open(f_name, \u0026#34;w+\u0026#34;).write(g_file.read()) #gzip对象用read()打开后，写入open()建立的文件里。 g_file.close() #关闭gzip对象 \u0026#39;\u0026#39;\u0026#39; def diyun_zip(file_name,root): if os.path.splitext(file_name)[1] == \u0026#34;.zip\u0026#34;: shutil.unpack_archive(root+\u0026#39;\\\\\u0026#39;+file_name) path = \u0026#39;\u0026#39; file_list = os.listdir(path) release_path = \u0026#39;\u0026#39; os.chdir(release_path) for root, dirs, files in os.walk(path): #print(files) for i in files: ##print(i) diy_un_zip(i,root) HEIC 转 JPG 图片转换\n展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import os import pillow_heif from PIL import Image def convert_heic_to_jpg(heic_path, jpg_path): \u0026#34;\u0026#34;\u0026#34;将 HEIC 格式的图像转换为 JPEG 格式并保存。\u0026#34;\u0026#34;\u0026#34; try: # 读取 HEIC 文件 heif_file = pillow_heif.read_heif(heic_path) # 创建 PIL 图像对象 image = Image.frombytes(mode=heif_file.mode, size=heif_file.size, data=heif_file.data) # 检查图像模式并转换为 RGB if image.mode == \u0026#39;RGBA\u0026#39;: image = image.convert(\u0026#39;RGB\u0026#39;) # 保存为 JPEG 格式 image.save(jpg_path, \u0026#34;JPEG\u0026#34;) print(f\u0026#34;Converted: {heic_path} to {jpg_path}\u0026#34;) except Exception as e: print(f\u0026#34;Error processing {heic_path}: {e}\u0026#34;) def recyle_convert(org_path, dst_path): \u0026#34;\u0026#34;\u0026#34;递归处理目录中的 HEIC 文件，将其转换为 JPEG 格式。\u0026#34;\u0026#34;\u0026#34; # 确保目标路径存在 if not os.path.exists(dst_path): os.makedirs(dst_path) # 遍历目录中的文件 for root, _, files in os.walk(org_path): for file in files: if file.lower().endswith(\u0026#39;.heic\u0026#39;): heic_file_path = os.path.join(root, file) jpg_file_path = os.path.join(dst_path, f\u0026#34;{os.path.splitext(file)[0]}.jpg\u0026#34;) convert_heic_to_jpg(heic_file_path, jpg_file_path) def main(): # 源路径和目标路径 org_path = \u0026#39;\u0026#39; dst_path = \u0026#39;\u0026#39; # 开始转换 recyle_convert(org_path, dst_path) if __name__ == \u0026#39;__main__\u0026#39;: main() 区分格式\n展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 import os import shutil # 循环读取HEIC格式照片，写入JPG def recyle_convert(org_path, dst_path): # 判断是不是目录 if os.path.isdir(org_path): file_list = os.listdir(org_path) print(file_list) for idx, file in enumerate(file_list): sub_path = os.path.join(org_path, file) recyle_convert(sub_path, dst_path) # 判断是不是文件 elif os.path.isfile(org_path): # 判断照片格式 if org_path.lower().endswith(\u0026#39;.jpg\u0026#39;) or org_path.lower().endswith(\u0026#39;.mov\u0026#39;): # 读取图片 path, filename = os.path.split(org_path) name, ext = os.path.splitext(filename) file_path = os.path.join(dst_path, \u0026#39;%s.jpg\u0026#39; % name) print(file_path) shutil.move(org_path, dst_path) else: print(org_path + \u0026#39;is error format!\u0026#39;) pass # 主函数入口 def main(): # dst path dst_path = \u0026#39;\u0026#39; #dst_path = os.getcwd() if os.path.exists(dst_path) is False: os.makedirs(dst_path) pass # org path org_path = \u0026#39;\u0026#39; # convert recyle_convert(org_path, dst_path) pass if __name__ == \u0026#39;__main__\u0026#39;: main() pass PPTX 转 1920x1080JPG 流程:\n判断源文件格式，如果是 ppt，转换为 jpg 再调整格式 如果是 png，转换为 jpg 再调整格式 如果是 jpg，直接调整格式 展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 # -*- codeing = utf-8 -*- # @Time : 2023 # @Author : asta # @File : trans v2.py # @software : python 3.6.5 idle import matplotlib.pyplot as plt from PIL import Image import comtypes.client import os import win32 import glob #逻辑 #判断源文件格式，如果是ppt，转换为jpg再调整格式 #如果是png，转换为jpg再调整格式 #如果是jpg，直接调整格式 #png转为jpg def pngtojpg(name): #如果是png，转换为jpg再调整格式 img = Image.open(name) if img.mode == \u0026#34;RGBA\u0026#34;: img = img.convert(\u0026#39;RGB\u0026#39;) print(name) imgname = os.path.splitext(name)[0]+ \u0026#34;.jpg\u0026#34; print(imgname) img.save(imgname) #ppt转为jgp def ppttojpg(openpath): #fileNames = glob.glob(openpath + r\u0026#39;\\*\u0026#39;) ## for fileName in fileNames: #将pa 文件夹中的文件删除。 ## os.remove( fileName) ## powerpoint = comtypes.client.CreateObject(\u0026#34;kwpp.Application\u0026#34;) #使用wps的接口 powerpoint = comtypes.client.CreateObject(\u0026#34;Powerpoint.Application\u0026#34;) #powerpoint.Visible = 0 ppt = powerpoint.Presentations.Open(openpath,WithWindow=0) # 另存为 pa = \u0026#39;\u0026#39; ppt.SaveAs(pa + \u0026#39;.jpg\u0026#39;, 17) # 退出 ppt.Close() powerpoint.Quit() def adjust_image(file_in, width, height, file_out): image = Image.open(file_in) #resized_image = image.resize((width, height), Image.ANTIALIAS) resized_image = image.resize((width, height), Image.Resampling.LANCZOS) resized_image.save(file_out) if __name__ == \u0026#39;__main__\u0026#39;: width = 1920 #调整的分辨率大小 height = 1080 n = 0 #设定路径 #源文件目录 my_dir = \u0026#39;\u0026#39; Files = os.listdir(my_dir) print(os.getcwd()) #遍历路径下的文件 for k in range(len(Files)): #将文件名 #Files[k]=os.path.splitext(Files[k])[1] print(Files[k]) file_in = \u0026#34;\u0026#34; if Files[k].endswith(\u0026#39;py\u0026#39;): continue elif Files[k].endswith(\u0026#39;jpg\u0026#39;): file_in = Files[k] elif Files[k].endswith(\u0026#39;png\u0026#39;): pngtojpg(Files[k]) file_in = os.path.splitext(Files[k])[0]+ \u0026#34;.jpg\u0026#34; elif Files[k].endswith(\u0026#39;pptx\u0026#39;): n = n+1 #cur_dir = os.path.dirname(os.path.abspath(Files[k])) #path1 = os.path.join(os.path.abspath(my_dir + os.path.sep + \u0026#34;..\u0026#34;), Files[k]) #print(path1) input_file_path = os.path.join(my_dir, Files[k]) print(input_file_path) ppttojpg(input_file_path) file_in = \u0026#34;Slide{0}.JPG\u0026#34;.format(n) file_out = \u0026#39;resized\u0026#39;+ file_in # 调整分辨率 adjust_image(file_in, width, height, file_out) ## Str2=[\u0026#39;.pptx\u0026#39;,\u0026#39;.png\u0026#39;,\u0026#39;.jpg\u0026#39;] ## print(set(Str2).intersection(set(Files))) ## ## if len(list(set(Str2).intersection(set(Files))))==len(Str2): ## print(\u0026#34;yes\u0026#34;) ## #return True ## else: ## print(\u0026#34;no\u0026#34;) ## #return False 读取 sticky note 存储并导出 需求: sticky note 有时候会出现同步问题，用户突然找不到原本的便签，需要通过%LocalAppData%\\Packages\\Microsoft.MicrosoftStickyNotes_8wekyb3d8bbwe\\LocalState\nmedia 便笺中图片数据保存路径 plum.sqlite 便笺中笔记内容的主数据库文件 plum.sqlite-shm plum.sqlite-wal 便笺访问主数据库缓存文件 展开代码 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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #导入sqllite3模块 import sqlite3 import re def write_info(info): with open(\u0026#39;sqlite_res_final2.txt\u0026#39;,\u0026#39;a+\u0026#39;,encoding=\u0026#39;utf-8\u0026#39;,errors=\u0026#39;ignore\u0026#39;) as f: f.write(info) f.close() # 1.硬盘上创建连接 con = sqlite3.connect(\u0026#39;plum.sqlite\u0026#39;) # 获取cursor对象 cur = con.cursor() # 执行sql创建表 #sql = \u0026#39;select text from Note\u0026#39; sql = \u0026#39;select * from Note\u0026#39; try: #reg = re.compile(r\u0026#34;^\\\\id=.*?\\s(.*)\u0026#34;) cur.execute(sql) person_all = cur.fetchall() # print(person_all) # 遍历 #person_all = [(\u0026#34;\\\\id=，)] #p = (\u0026#34;\\\\id=) #p[0] = \u0026#34;\\\\id= #print(person_all[0][0]) ## t = person_all[0][0] ## resub = re.sub(r\u0026#39;(\\\\id=.*?\\s)\u0026#39;,\u0026#34;\u0026#34;,t) ## print(resub) for p in person_all: resub = re.sub(r\u0026#39;(\\\\id=.*?\\s)\u0026#39;,\u0026#34;\u0026#34;,p[0]) #print(resub) #print(reg.findall(p[0])) #write_info(p[0]) #print(p[0]) write_info(resub) except Exception as e: print(e) print(\u0026#39;创建表失败\u0026#39;) finally: # 关闭游标 cur.close() # 关闭连接 con.close() 苹果手机的相片按照时间排序处理 读取 aae 格式 展开代码 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 from PIL import Image from PIL.ExifTags import TAGS def get_image_date(image_path): try: with Image.open(image_path) as img: exif_data = img._getexif() if exif_data is not None: for tag, value in exif_data.items(): tag_name = TAGS.get(tag) if tag_name == \u0026#39;DateTimeOriginal\u0026#39;: return value except (IOError, AttributeError): pass return None # 指定AAE文件路径 aae_file_path = \u0026#39;\u0026#39; # 构建对应的照片文件路径 photo_file_path = aae_file_path.replace(\u0026#39;.aae\u0026#39;, \u0026#39;\u0026#39;) # 获取照片日期 photo_date = get_image_date(photo_file_path) if photo_date is not None: print(\u0026#34;照片日期：\u0026#34;, photo_date) else: print(\u0026#34;无法获取照片日期信息。\u0026#34;) 读取相片时间 展开代码 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 32 33 34 35 36 37 38 39 from PIL import Image from PIL.ExifTags import TAGS import os def get_image_date(image_path): try: with Image.open(image_path) as img: exif_data = img._getexif() if exif_data is not None: for tag, value in exif_data.items(): tag_name = TAGS.get(tag) #print(tag_name,value) if tag_name == \u0026#39;DateTimeOriginal\u0026#39;: return value except (IOError, AttributeError): pass return None my_dir = \u0026#39;\u0026#39; Files = os.listdir(my_dir) for k in range(len(Files)): print(Files[k]) # 指定图片路径 image_path = os.path.join(my_dir, Files[k]) # 获取图片日期 image_date = get_image_date(image_path) if image_date is not None: print(\u0026#34;图片日期：\u0026#34;, image_date) else: print(\u0026#34;无法获取图片日期信息。\u0026#34;) ","date":"2022-06-10T00:00:00Z","permalink":"https://cancanneed64.github.io/p/python%E5%8A%9E%E5%85%AC%E5%B0%8F%E5%B7%A5%E5%85%B7/","title":"Python办公小工具"},{"content":"simple markdown Typora 官方介绍\n目录\u0026quot;[TOC]\u0026quot; [TOC]\n1. 常用标题/列表 \u0026ldquo;#” 每级标题 \u0026ldquo;*\u0026ldquo;无序列表\n无序 \u0026ldquo;1.\u0026ldquo;有序列表\n有序 \u0026ldquo;-[]\u0026rdquo; - \u0026ldquo;[x]\u0026ldquo;任务列表\n1 2 2.插入分段 \u0026ldquo;```\u0026ldquo;插入代码\n\u0026ldquo;\u0026gt;\u0026ldquo;插入话题\n| First Header | Second Header | 插入表格，标题行:表示对齐\n\u0026ldquo;[^footnote]:\u0026rdquo; 脚注\n\u0026ldquo;***\u0026ldquo;或\u0026rdquo;\u0026mdash;\u0026ldquo;分段\n\u0026ldquo;\u0026ldquo;插入网址\n\u0026ldquo;[exam][id]\u0026rdquo; \u0026ldquo;[id]:\u0026rdquo; 跳转\n\u0026ldquo;\u0026lt;\u0026gt;\u0026ldquo;超链接\n\u0026ldquo;\u0026ldquo;插入图片\n\u0026ldquo;::\u0026ldquo;表情\n3.字体 \u0026ldquo;*\u0026ldquo;斜体\n\u0026ldquo;**\u0026ldquo;加粗\n\u0026ldquo;`\u0026ldquo;代码\n\u0026ldquo;~~\u0026ldquo;划掉\n\u0026ldquo;\u0026ldquo;下划线\n\u0026ldquo;====\u0026ldquo;高亮 @\n","date":"2022-06-07T00:00:00Z","permalink":"https://cancanneed64.github.io/p/markdown/","title":"Markdown"},{"content":"正文测试 而这些并不是完全重要，更加重要的问题是， 带着这些问题，我们来审视一下学生会退会。 既然如何， 对我个人而言，学生会退会不仅仅是一个重大的事件，还可能会改变我的人生。 我们不得不面对一个非常尴尬的事实，那就是， 可是，即使是这样，学生会退会的出现仍然代表了一定的意义。 学生会退会，发生了会如何，不发生又会如何。 经过上述讨论， 生活中，若学生会退会出现了，我们就不得不考虑它出现了的事实。 学生会退会，到底应该如何实现。 这样看来， 在这种困难的抉择下，本人思来想去，寝食难安。 对我个人而言，学生会退会不仅仅是一个重大的事件，还可能会改变我的人生。 就我个人来说，学生会退会对我的意义，不能不说非常重大。 莎士比亚曾经提到过，人的一生是短的，但如果卑劣地过这一生，就太长了。这似乎解答了我的疑惑。 莫扎特说过一句富有哲理的话，谁和我一样用功，谁就会和我一样成功。这启发了我， 对我个人而言，学生会退会不仅仅是一个重大的事件，还可能会改变我的人生。 学生会退会，到底应该如何实现。 一般来说， 从这个角度来看， 这种事实对本人来说意义重大，相信对这个世界也是有一定意义的。 在这种困难的抉择下，本人思来想去，寝食难安。 了解清楚学生会退会到底是一种怎么样的存在，是解决一切问题的关键。 一般来说， 生活中，若学生会退会出现了，我们就不得不考虑它出现了的事实。 问题的关键究竟为何？ 而这些并不是完全重要，更加重要的问题是。\n奥斯特洛夫斯基曾经说过，共同的事业，共同的斗争，可以使人们产生忍受一切的力量。　带着这句话，我们还要更加慎重的审视这个问题： 一般来讲，我们都必须务必慎重的考虑考虑。 既然如此， 这种事实对本人来说意义重大，相信对这个世界也是有一定意义的。 带着这些问题，我们来审视一下学生会退会。 我认为， 我认为， 在这种困难的抉择下，本人思来想去，寝食难安。 问题的关键究竟为何？ 每个人都不得不面对这些问题。 在面对这种问题时， 要想清楚，学生会退会，到底是一种怎么样的存在。 我认为， 既然如此， 每个人都不得不面对这些问题。 在面对这种问题时， 那么， 我认为， 学生会退会因何而发生。\n引用 思念是最暖的忧伤像一双翅膀\n让我停不了飞不远在过往游荡\n不告而别的你 就算为了我着想\n这么沉痛的呵护 我怎么能翱翔\n最暖的憂傷 - 田馥甄\n图片 1 2 3 ![Photo by Florian Klauer on Unsplash](florian-klauer-nptLmg6jqDo-unsplash.jpg) ![Photo by Luca Bravo on Unsplash](luca-bravo-alS7ewQ41M8-unsplash.jpg) ![Photo by Helena Hertz on Unsplash](helena-hertz-wWZzXlDpMog-unsplash.jpg) ![Photo by Hudai Gayiran on Unsplash](hudai-gayiran-3Od_VKcDEAA-unsplash.jpg) 相册语法来自 Typlog\n","date":"2020-09-09T00:00:00Z","image":"https://cancanneed64.github.io/p/test-chinese/helena-hertz-wWZzXlDpMog-unsplash_hu45a5e3ad5e058da6a00650ed8fd40bea_15530_120x120_fill_q75_box_smart1.jpg","permalink":"https://cancanneed64.github.io/p/test-chinese/","title":"Chinese Test"},{"content":"This article offers a sample of basic Markdown syntax that can be used in Hugo content files, also it shows whether basic HTML elements are decorated with CSS in a Hugo theme.\nHeadings The following HTML \u0026lt;h1\u0026gt;—\u0026lt;h6\u0026gt; elements represent six levels of section headings. \u0026lt;h1\u0026gt; is the highest section level while \u0026lt;h6\u0026gt; is the lowest.\nH1 H2 H3 H4 H5 H6 Paragraph Xerum, quo qui aut unt expliquam qui dolut labo. Aque venitatiusda cum, voluptionse latur sitiae dolessi aut parist aut dollo enim qui voluptate ma dolestendit peritin re plis aut quas inctum laceat est volestemque commosa as cus endigna tectur, offic to cor sequas etum rerum idem sintibus eiur? Quianimin porecus evelectur, cum que nis nust voloribus ratem aut omnimi, sitatur? Quiatem. Nam, omnis sum am facea corem alique molestrunt et eos evelece arcillit ut aut eos eos nus, sin conecerem erum fuga. Ri oditatquam, ad quibus unda veliamenimin cusam et facea ipsamus es exerum sitate dolores editium rerore eost, temped molorro ratiae volorro te reribus dolorer sperchicium faceata tiustia prat.\nItatur? Quiatae cullecum rem ent aut odis in re eossequodi nonsequ idebis ne sapicia is sinveli squiatum, core et que aut hariosam ex eat.\nBlockquotes The blockquote element represents content that is quoted from another source, optionally with a citation which must be within a footer or cite element, and optionally with in-line changes such as annotations and abbreviations.\nBlockquote without attribution Tiam, ad mint andaepu dandae nostion secatur sequo quae. Note that you can use Markdown syntax within a blockquote.\nBlockquote with attribution Don\u0026rsquo;t communicate by sharing memory, share memory by communicating.\n— Rob Pike1\nTables Tables aren\u0026rsquo;t part of the core Markdown spec, but Hugo supports supports them out-of-the-box.\nName Age Bob 27 Alice 23 Inline Markdown within tables Italics Bold Code italics bold code A B C D E F Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus ultricies, sapien non euismod aliquam, dui ligula tincidunt odio, at accumsan nulla sapien eget ex. Proin eleifend dictum ipsum, non euismod ipsum pulvinar et. Vivamus sollicitudin, quam in pulvinar aliquam, metus elit pretium purus Proin sit amet velit nec enim imperdiet vehicula. Ut bibendum vestibulum quam, eu egestas turpis gravida nec Sed scelerisque nec turpis vel viverra. Vivamus vitae pretium sapien Code Blocks Code block with backticks 1 2 3 4 5 6 7 8 9 10 \u0026lt;!doctype html\u0026gt; \u0026lt;html lang=\u0026#34;en\u0026#34;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;Example HTML5 Document\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;p\u0026gt;Test\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; Code block indented with four spaces \u0026lt;!doctype html\u0026gt; \u0026lt;html lang=\u0026quot;en\u0026quot;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026quot;utf-8\u0026quot;\u0026gt; \u0026lt;title\u0026gt;Example HTML5 Document\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;p\u0026gt;Test\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; Code block with Hugo\u0026rsquo;s internal highlight shortcode 1 2 3 4 5 6 7 8 9 10 \u0026lt;!doctype html\u0026gt; \u0026lt;html lang=\u0026#34;en\u0026#34;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;Example HTML5 Document\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;p\u0026gt;Test\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; Diff code block 1 2 3 4 5 [dependencies.bevy] git = \u0026#34;https://github.com/bevyengine/bevy\u0026#34; rev = \u0026#34;11f52b8c72fc3a568e8bb4a4cd1f3eb025ac2e13\u0026#34; - features = [\u0026#34;dynamic\u0026#34;] + features = [\u0026#34;jpeg\u0026#34;, \u0026#34;dynamic\u0026#34;] List Types Ordered List First item Second item Third item Unordered List List item Another item And another item Nested list Fruit Apple Orange Banana Dairy Milk Cheese Other Elements — abbr, sub, sup, kbd, mark GIF is a bitmap image format.\nH2O\nXn + Yn = Zn\nPress CTRL + ALT + Delete to end the session.\nMost salamanders are nocturnal, and hunt for insects, worms, and other small creatures.\nHyperlinked image The above quote is excerpted from Rob Pike\u0026rsquo;s talk during Gopherfest, November 18, 2015.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2019-03-11T00:00:00Z","image":"https://cancanneed64.github.io/p/markdown-syntax-guide/pawel-czerwinski-8uZPynIu-rQ-unsplash_hud7e36f7e20e71be184458283bdae4646_55974_120x120_fill_q75_box_smart1.jpg","permalink":"https://cancanneed64.github.io/p/markdown-syntax-guide/","title":"Markdown Syntax Guide"},{"content":"Lorem est tota propiore conpellat pectoribus de pectora summo.\nRedit teque digerit hominumque toris verebor lumina non cervice subde tollit usus habet Arctonque, furores quas nec ferunt. Quoque montibus nunc caluere tempus inhospita parcite confusaque translucet patri vestro qui optatis lumine cognoscere flos nubis! Fronde ipsamque patulos Dryopen deorum.\nExierant elisi ambit vivere dedere Duce pollice Eris modo Spargitque ferrea quos palude Rursus nulli murmur; hastile inridet ut ab gravi sententia! Nomine potitus silentia flumen, sustinet placuit petis in dilapsa erat sunt. Atria tractus malis.\nComas hunc haec pietate fetum procerum dixit Post torum vates letum Tiresia Flumen querellas Arcanaque montibus omnes Quidem et Vagus elidunt The Van de Graaf Canon\nMane refeci capiebant unda mulcebat Victa caducifer, malo vulnere contra dicere aurato, ludit regale, voca! Retorsit colit est profanae esse virescere furit nec; iaculi matertera et visa est, viribus. Divesque creatis, tecta novat collumque vulnus est, parvas. Faces illo pepulere tempus adest. Tendit flamma, ab opes virum sustinet, sidus sequendo urbis.\nIubar proles corpore raptos vero auctor imperium; sed et huic: manus caeli Lelegas tu lux. Verbis obstitit intus oblectamina fixis linguisque ausus sperare Echionides cornuaque tenent clausit possit. Omnia putatur. Praeteritae refert ausus; ferebant e primus lora nutat, vici quae mea ipse. Et iter nil spectatae vulnus haerentia iuste et exercebat, sui et.\nEurytus Hector, materna ipsumque ut Politen, nec, nate, ignari, vernum cohaesit sequitur. Vel mitis temploque vocatus, inque alis, oculos nomen non silvis corpore coniunx ne displicet illa. Crescunt non unus, vidit visa quantum inmiti flumina mortis facto sic: undique a alios vincula sunt iactata abdita! Suspenderat ego fuit tendit: luna, ante urbem Propoetides parte.\n","date":"2019-03-09T00:00:00Z","image":"https://cancanneed64.github.io/p/placeholder-text/matt-le-SJSpo9hQf7s-unsplash_hu958d513eeefe5556a31d065479ecc5ac_14205_120x120_fill_q75_box_smart1.jpg","permalink":"https://cancanneed64.github.io/p/placeholder-text/","title":"Placeholder Text"},{"content":"Mathematical notation in a Hugo project can be enabled by using third party JavaScript libraries.\nIn this example we will be using KaTeX\nCreate a partial under /layouts/partials/math.html Within this partial reference the Auto-render Extension or host these scripts locally. Include the partial in your templates like so: 1 2 3 {{ if or .Params.math .Site.Params.math }} {{ partial \u0026#34;math.html\u0026#34; . }} {{ end }} To enable KaTeX globally set the parameter math to true in a project\u0026rsquo;s configuration To enable KaTeX on a per page basis include the parameter math: true in content files Note: Use the online reference of Supported TeX Functions\nExamples Inline math: $\\varphi = \\dfrac{1+\\sqrt5}{2}= 1.6180339887…$\nBlock math: $$ \\varphi = 1+\\frac{1} {1+\\frac{1} {1+\\frac{1} {1+\\cdots} } } $$\n","date":"2019-03-08T00:00:00Z","permalink":"https://cancanneed64.github.io/p/math-typesetting/","title":"Math Typesetting"},{"content":"Emoji can be enabled in a Hugo project in a number of ways.\nThe emojify function can be called directly in templates or Inline Shortcodes.\nTo enable emoji globally, set enableEmoji to true in your site\u0026rsquo;s configuration and then you can type emoji shorthand codes directly in content files; e.g.\n🙈 :see_no_evil: 🙉 :hear_no_evil: 🙊 :speak_no_evil:\nThe Emoji cheat sheet is a useful reference for emoji shorthand codes.\nN.B. The above steps enable Unicode Standard emoji characters and sequences in Hugo, however the rendering of these glyphs depends on the browser and the platform. To style the emoji you can either use a third party emoji font or a font stack; e.g.\n1 2 3 .emoji { font-family: Apple Color Emoji, Segoe UI Emoji, NotoColorEmoji, Segoe UI Symbol, Android Emoji, EmojiSymbols; } ","date":"2019-03-05T00:00:00Z","image":"https://cancanneed64.github.io/p/emoji-support/the-creative-exchange-d2zvqp3fpro-unsplash_huf941de4769045cdfa8c9ee7036519a2a_35369_120x120_fill_q75_box_smart1.jpg","permalink":"https://cancanneed64.github.io/p/emoji-support/","title":"Emoji Support"}]