V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  kuanat  ›  全部回复第 6 页 / 共 9 页
回复总数  164
1  2  3  4  5  6  7  8  9  
@zhwguest #8

git tag 是与 repo 相关的,/old/dep 和 /new/dep 是相互独立的,我不确定你说的冲突,只要同一个 repo 里面 tag 唯一就可以。

一般不建议 => 左边带版本,除非你的项目永远不升级依赖。如果左边带版本 v1.2.3 ,就代表只有这个特定版本会被替换,但是上游更新 v1.2.4 然后你 go get -u 升级了依赖,那 v1.2.3 的替换就失效了。不带版本就是所有版本都会替换。

右边只有替换成本地 repo 的时候才可以不带版本。
153 天前
回复了 Kholin 创建的主题 分享创造 我写了个类 Hacker News 的社区
chi 确实非常好用,无依赖,兼容 net/http 。小项目里极其灵活,大项目里无缝替换。
153 天前
回复了 hkhk366 创建的主题 程序员 everything 索引原理探讨
@hkhk366 #20

我知道问题出在哪里了,是“全文搜索”这个词有歧义,我理解成了 document retrieval 而你想表达的是 substring indexing 。

回到“建索引”这个事情上,我说下 everything 的做法。很早之前逆向过,有个大致的印象,不保证适用于现在的版本。即使不逆向,很多思路也能推理出来。

1.
持久化层面上,本地数据库就是 MFT 解析之后换了个私有格式,文件夹和文件分开,排序之后保存。目的是加速冷启动之后的数据重建。这个排序反正是一次性的,后续根据 Journal 来更新。

重点在于它是用什么样的数据结构来描述文件系统的树型关系,考虑到查询是经常需要按某个子路径做筛选的,所以文件和文件夹都会记录 parent 的指向。这里 parent 就是比较重要的索引之一。

2.
其他可以检索的字段的索引都是在内存中重建的,比如文件大小、扩展名和时间戳这些。

就是类似 sql 那种对某个字段建索引,说白了就是换个结构重写了一份。印象用的是 B+ 树而不是 hash ,因为 hash 不太适合做范围查询。

3.
字符串匹配明显是基于正则的。所以像文件名、路径名是没必要做“索引”的,都是全量匹配。

一般来说,解释型的正则引擎都比较慢。编译型的正则引擎要么是编译成 bytecode 然后 JIT 运行,要么是编译时就把所有匹配路径都预先编译好。后面这个方式只适用于正则比较简单的情况,everything 是支持 \n 回溯的,生成的二进制文件也很小,不太可能是后面的做法。

手写正则引擎这个事情我没做过,理论上要建 NFA 然后转成 DFA ,这个过程比较麻烦。运行的时候只需要对 pattern 做一次,然后就可以匹配所有字符串了,对于文件名 substring matching 这个场景是非常合适的。

4.
everything 没有用 pcre 的原因应该是它不需要“通用”的正则引擎(没有替换的需求,也就不需要记录中间状态),自己写一个性能会更好。

另外它不支持模糊查询,我理解这里不是个性能问题,计算 Levenshtein 距离没有太大开销,但是结果排序的开销会很大。



顺便一提逆向的事情。具体到 everything 上面,主要还是靠静态分析。因为这个程序写得非常好,又没有刻意做混淆,尽管 c 程序非 debug 版是不带符号表的,但是通过字符串 xrefs 很容易理解并还原调用逻辑,特别是你大概能猜到作者思路的情况下。

另外用逆向手段还原算法有两种,比如加密狗之类的逻辑一般需要硬怼,但像 everything 这种主要靠猜,根据传参和返回还有逻辑,从语义上理解操作过程,毕竟它大概率是某个已知算法,而不是作者自创的。
做个不负责任的猜测。

我印象 Windows 微信自带的那个图片查看器是单独用 C 写的,底层是 GDI+,内部实现是支持 DPI 缩放的。

这就意味着它不是以物理 pixel 的方式来显示图片的,所以缩放的时候会存在逻辑像素和物理像素的比例转换,由此内部会有 rounding ,如果要正常显示图片其实是需要插值的。

除非图片恰好位于画布的 (0,0) 位置,按 Windows 显示的坐标系就是左上角。这个逻辑在大部分时候会感知不到,因为对于不需要缩放的场景,画布和图片是一样大的,没有插值过程。严格来说还是会走一遍插值流程,但是没有 rounding 。

但是对于需要缩放的场景,图片不会位于 (0,0) 位置,这时候插值的效果就体现出来了。默认的插值逻辑是 Linear 算法,这个很容易察觉到模糊。

这个行为比较反直觉,把图片放到左上角,它就是清晰的。一旦相对画布有个 (x,y) 偏移,即使没有任何缩放,也会导致模糊。

上面都是凭印象写的,要验证的话看一下那个图片查看器的导入函数,如果没有 SetInterpolationMode 的调用基本就是这个问题没跑了。


PS

其实像文本编辑器、图片查看器这类应用都是很难写的,细节非常多而且原理都非常基础,不是简单调用个 API 的事情。上面说的这个问题,正常科班出身的话,有一点图形学基础大致就能反应过来问题出在哪里,但是没有接触过就几乎想象不到。

话说回来,反正大家都是草台班子,又不是不能用……
153 天前
回复了 hkhk366 创建的主题 程序员 everything 索引原理探讨
其实你这个思路偏了啊,当你意识到 5ms 连文件 IO 都完成不了的时候,应该考虑这不是个算法问题而是个策略问题。



官方页面上已经说得很清楚了,因为关键内容不多,我就直接复制过来:

来自 https://www.voidtools.com/support/everything/searching/

Content Searching

Warning: content searching is extremely slow.

File content is not indexed.

Please combine content: functions with other filters for the best performance.

Content search functions:
content:<text> Search file content using the associated iFilter. If no iFilter exists UTF-8 content is used.
ansicontent:<text> File contents are treated as ANSI text.
utf8content:<text> File contents are treated as UTF-8 text.
utf16content:<text> File contents are treated as UTF-16 (Unicode) text.
utf16becontent:<text> File contents are treated as UTF-16 (Big Endian) text.

另外 https://www.voidtools.com/forum/viewtopic.php?t=9793 这里有更详细的说明。

Everything will keep content in memory.
Content indexing is intended for indexing user documents only.
I do not recommend indexing over 1GB of text.
For the best performance, set an include only folder.

By default, Everything will index the following file types for content:
*.doc;*.docx;*.pdf;*.txt;*.xls;*.xlsx



这 5ms 就做了一个内存数据库的检索,而且数量级并不是你想象中的 100 万。你在没有验证的情况下,就认为它扫描了文件,所以思路跑偏了。即使没有一点逆向基础,也有很简单的办法知道一个程序运行的时候打开读取了哪些文件,如果你验证了这一点,估计已经找到答案了。

所有的“索引”类算法,核心思想并不在“算”上面,而在于将原始数据结构以一种易于被检索的数据格式存储。换句话说,之所以索引可以加速搜索,是因为提前生成了要查询的结果。

Windows 自身有个索引服务,但是绝大多数文件格式都是二进制的,在不了解它的文件结构的情况下,是没有办法进行文本搜索的。所以 Windows 提供了一个 IFilter 机制,由文件格式的创建者,主动暴露可以被索引的信息。然后再通过后台服务对于相关的文件格式进行索引,首次全盘索引完成之后,只有文件内容发生变化,才会触发索引更新。几大操作系统的索引都是相同的机制。

Everything 的说明就是这样一个意思,它会在内存索引少数路径下的特定格式的文件,为其建立文本内容索引。后续的搜索只是内存搜索引。至于全文匹配到底用了什么算法,是不是有指令集优化,是另外的事情,到了这个层面,只要思路对,算法是不是最优化已经不重要了。( Everything 是 C 写的,但是没有什么混淆加密,稍微逆向一下大概就能弄清楚。)
154 天前
回复了 happmaoo 创建的主题 Linux 终于可以登陆网页微信了
154 天前
回复了 happmaoo 创建的主题 Linux 终于可以登陆网页微信了
@pakro888 #56

我又重新看了一下,实际上只有登录过程有校验,实际使用的时候是没有的。所以只需要匹配 /cgi-bin/mmwebxxxx-bin/webxxxxnewloginpage 这个路径做修改就可以了。

估计腾讯现在比较后悔,当初就不该做 web 版本,做了 web 版不说,失误就失误在又套壳 web 做了 UOS 版,这下彻底摆脱不掉了。结果就是现在所有的微信机器人都是走的 UOS 这个协议。

腾讯不想让人用 web 版,就从账号上做限制,明明有 web 版就是不给用。但是腾讯没法限制 UOS 这个客户端,好在用 UOS 的人还是比较少的,只是这样还是管不住机器人。顺便一说,UOS 这个版本还是 MIT 协议发布的。

UOS 版本是有发行版校验的,这也就是 extspam 这个字段的来源。

所以去年的时候,有人把 UOS 版本重新打包给其他发行版用,就把 /etc/lsb-release 给改了。之前 V2EX 上有过讨论帖子 https://v2ex.com/t/858659

研究一下就会发现,这个特殊的 extspam 竟然是静态的……这个失误就比较离谱了,也就是说根本起不到防滥用的作用。所以现在用简单的网页扩展,修改一下请求头就能一直用。

估计是当初负责 UOS 版本的人没有什么网络开发经验,缺少基本的校验要动态化的常识。另外本地防逆向的水平也很弱,生成逻辑都在一个 so 库里,只做了字符串混淆,混淆还是最简单的 xor 。
@zhwguest #4

可能之前没表达清楚,这里应该不存在冲突的。


比如说你一开始用的 /old/dep 依赖,那么在 go.mod 里面就会有

require github.com/old/dep v1.2.3

只要你不升级依赖,那么整个项目一直会用 /old/dep v1.2.3

然后你 fork 了 /new/dep 出来,并且给 /new/dep 打了版本 v1.2.3
这个时候增加一句

replace github.com/old/dep v1.2.3 => github.com/new/dep v1.2.3

就可以了。

假如 /old/dep 更新过后,你发现没有必要用 /new/dep 了,那就把上面的删掉,然后更新依赖到比如说 /new/dep v1.2.4 ,不管是 go get -u 还是手动修改 go.mod ,最终 go.mod 都会是

require github.com/old/dep v1.2.4

假如你还是要用 /new/dep ,不管你是继续在 /new/dep 上修改,还是合并了 /old/dep 的上游更新,然后打了 v1.2.4 的版本,只需要修改 replace 这一句变成

replace github.com/old/dep v1.2.3 => github.com/new/dep v1.2.4

但是 require 那一句还保持

require github.com/old/dep v1.2.3



也就是说,只要你不主动升级 /old/dep 的版本,那就只需要修改 replace 的目标版本。至于 /new/dep 怎么打版本 tag 是随意的。

之前说保持版本号一致只是应对两种场景,一种是 /new/dep 作为临时开发测试分支,有可能被 /old/dep 合并,当 /old/dep 更新之后又会回到 /old/dep 上面去。另一种是 /new/dep 是因为某些原因不可能被 /old/dep 合并,但是修改可以通过 patch 的方式自动化,上游 /old/dep 发布一个版本,下游 /new/dep 就发布一个对应的修改版。



默认情况下,github 的 fork 功能,是不带 tag 的,所以你可以在 fork 里面重新任意标记版本。假如你不想处理 tag 相关的事情,也可以完全不理会。这种情况下,replace 后面改成 /new/dep 就行了,go.sum 会用 v0.0.0-timestamp-commit_hash 的方式来唯一确定。
155 天前
回复了 happmaoo 创建的主题 Linux 终于可以登陆网页微信了
@BaiLinfeng #41

前面是手机回复的,开电脑看了一下。

登录链接用:

https://wx.qq.com/?&lang=zh_CN&target=t

然后用个可以改 header 的扩展,匹配一下 https://wx.qq.com/* 的请求,添加两个字段

client-version = 2.0.0

extspam =
Go8FCIkFEokFCggwMDAwMDAwMRAGGvAESySibk50w5Wb3uTl2c2h64jVVrV7gNs06GFlWplHQbY/5FfiO++1yH4ykCyNPWKXmco+wfQzK5R98D3so7rJ5LmGFvBLjGceleySrc3SOf2Pc1gVehzJgODeS0lDL3/I/0S2SSE98YgKleq6Uqx6ndTy9yaL9qFxJL7eiA/R3SEfTaW1SBoSITIu+EEkXff+Pv8NHOk7N57rcGk1w0ZzRrQDkXTOXFN2iHYIzAAZPIOY45Lsh+A4slpgnDiaOvRtlQYCt97nmPLuTipOJ8Qc5pM7ZsOsAPPrCQL7nK0I7aPrFDF0q4ziUUKettzW8MrAaiVfmbD1/VkmLNVqqZVvBCtRblXb5FHmtS8FxnqCzYP4WFvz3T0TcrOqwLX1M/DQvcHaGGw0B0y4bZMs7lVScGBFxMj3vbFi2SRKbKhaitxHfYHAOAa0X7/MSS0RNAjdwoyGHeOepXOKY+h3iHeqCvgOH6LOifdHf/1aaZNwSkGotYnYScW8Yx63LnSwba7+hESrtPa/huRmB9KWvMCKbDThL/nne14hnL277EDCSocPu3rOSYjuB9gKSOdVmWsj9Dxb/iZIe+S6AiG29Esm+/eUacSba0k8wn5HhHg9d4tIcixrxveflc8vi2/wNQGVFNsGO6tB5WF0xf/plngOvQ1/ivGV/C1Qpdhzznh0ExAVJ6dwzNg7qIEBaw+BzTJTUuRcPk92Sn6QDn2Pu3mpONaEumacjW4w6ipPnPw+g2TfywJjeEcpSZaP4Q3YV5HG8D6UjWA4GSkBKculWpdCMadx0usMomsSS/74QgpYqcPkmamB4nVv1JxczYITIqItIKjD35IGKAUwAA==

估计搜一下“微信 extspam”就能找到,基于 UOS 的都应该是这个方法。
155 天前
回复了 happmaoo 创建的主题 Linux 终于可以登陆网页微信了
我一直在用模拟 UOS 那个 electron 版本的方式,大概一年了没有问题。

登录请求抓下来印象是三个特殊 header 字段,然后登录链接加个 &target=t 就行。
155 天前
回复了 abcfreedom 创建的主题 Android note12turbo 刷哪个系统比较靠谱
@justincnn #11

厉害的是做 rom 的人啊,其他机型上 xda 看一下,基本上找到一个就能全找到了。

我这是更新一批开发测试机,特意挑的刷机最方便的 n12t ,其他机型可能没这么多选择。
156 天前
回复了 abcfreedom 创建的主题 Android note12turbo 刷哪个系统比较靠谱
t.me/Poco_F5Official

这里面基本上有所有的第三方。
156 天前
回复了 suqiuluck 创建的主题 程序员 有没有自己电脑上跑大模型的大佬啊
@shuiguomayi #18

是 RTX 4060 Mobile ,笔记本上用的,8GB 显存。说的是开发验证这种需求,你需要训练一个模型,先在本地写个小规模的验证程序,然后放到服务器上去跑大数据集。并不是常见的用模型来推理,推理这个需求还是 12GB 起步吧,8GB 只能跑一些简化或者降低精度的模型,速度也不太理想。

每一代 60 显卡都会有个显存略大的版本,可以理解为 nvidia 推广 cuda 生态用的,因为这个级别上加显存对游戏性能几乎没什么影响。说移动版 4060 是因为它相对 3060 加了显存,而且能耗比很好,市面上的笔记本能做到 5000 块,比起台式机性价比可以的。
158 天前
回复了 0xTSO 创建的主题 Android 安卓 app 同步功能实现原理
先需要弄清楚几个概念:

- jetpack compose 重点是 ui 和本地数据状态的同步
- 安卓 app 的同步指的是多客户端以及服务器之间数据副本的同步
- 断网可用这个特性一般用 local first 来描述

你的问题是 local first 实现方式,和用不用 jetpack compose 与是不是安卓没有关系。

这个问题的答案和业务逻辑强相关,没有普适的模式。楼上提到的 OT/CRDT 适合于多人同时编辑文档的场景,并不适合 local first 意义上的同步。

另外你举例的 app 可能在多端同步实现上没有想象中那么好。
158 天前
回复了 kuanat 创建的主题 Go 编程语言 分享一些 Go 在全栈开发中的经验
@ila #36

你的问题比较笼统,建议开个帖子详细描述一下,这里可能会有不少人愿意解答。

我不确定你说的配置是哪方面的。如果是说 gocv 依赖这些,既然目标平台是 windows ,那我建议 MSYS2 搞一套 MinGW 环境,按官方指引构建 opencv 然后直接在 windows 平台开发。

如果有跨平台需求,比如 linux 开发部署到 windows 这样,和一般交叉编译没什么区别。需要 linux 安装 MinGW 交叉工具链,然后指定 GOOS/GOARCH/CGO 编译就行。主要是 CC/CXX 指定交叉工具链,CGO_CPPFLAGS/CGO_LDFLAGS 指定 opencv 依赖。这个方法对于 pc 开发 arm 平台目标也一样。

也可以考虑预编译好的 opencv 二进制库,动态链接。

如果你说的是 windows 调用摄像头获得视频流,这个事情比较复杂,opencv 虽然有非常简单的抽象,但远远不够用。

主要看摄像头设备是不是足够标准,一般的 ip camera 走网络数据最简单;如果能把自己注册成摄像头的要走 avicap32 这套 Win32 API ,然后用消息机制来通信;如果设备比较特殊,那就只能硬怼厂家 sdk 了。

理论上 WinRT 的 MediaCaputre API 也能用,只是我不确定有没有人做相关的 bindings 。这个思路有一些曲线救国的方式,比如打包 Electron ,然后调用 Electron API 。它内部就是调用的系统 API ,但不需要你自己封装转换了。

剩下应该都是 opencv 自己就能解决的。建议有条件还是 C# 开发吧,估计很可能还是要做界面的,全用 go 写可能不如 C# 效率高。当然如果目标平台是 linux 那用 go 就简单多了,走 v4l2 能解决大部分问题。
用你方法一的思路,replace 是支持版本号映射的,比如

require github.com/old/dep v1.2.3 => github.com/new/dep v1.2.4

另外也可以 github.com/new/dep@dev 指定分支。

你说的方法二有个问题:就像你说的,如果它包含子模块,这些子模块还是会引用 /old/dep ,那 fork 出来需要修改的地方就非常多。

具体怎么做取决于 /new/dep 这个 fork 的定位。仅仅打个补丁,需要定期合并上游 /old/dep 那就用方法一。如果是要大刀阔斧另起炉灶,那就方法二吧。

关于版本号,我的建议是保持一致。就是你在 v1.2.3 上的改版,就用 v1.2.3 发布,特别是你仅仅要做个小改动的时候。不过这个事情不重要,你清楚对应的版本就可以了,而且几乎做不到一一对应。

另外这里要注意 go sum 机制,改版 /new/dep 发布之后 github.com/new/dep v1.2.3 校验信息会被 google 缓存。这个时候如果对 /new/dep 做了多次修改,就只能升版本或者换分支了。
@LemonLeon #36

我做了个简单推理,可能需要你实际测试一下。

既然加 cpu 和内存不影响 rps 说明大概率瓶颈应该是在 IO 了。我大致看了一下处理流程,涉及到 IO 的操作就是日志相关的。

https://github.com/songquanpeng/one-api/blob/366b82128f89a328f096da6951cbafebb6b0060f/controller/relay-text.go#L410

这段代码主要功能是在请求结束后结算用量,然后记录。记录的内容本地文件有一份,数据库有一份。本地那一份肯定比数据库快,所以不考虑了。

数据库 IO 涉及三个操作:

https://imgur.com/a/OAPZXsl

如果是默认配置( common.LogConsumeEnabled=true ),RecordConsumeLog 会产生一次插入操作;

默认 BATCH_UPDATE_ENABLED=false ,那么 UpdateUserUsedQuotaAndRequestCount 和 UpdateChannelUsedQuota 各自都会产生一次查找更新操作。

等于说每个请求都伴随三次数据库写操作。默认 SQL_MAX_OPEN_CONNS = 1000 ,理论并发在 333 左右,和实测比较接近。

当然前提是这三个数据库操作能够在 1s 之内完成(大概率是的)。这个事情不太容易确定,因为 locust 记录的是完成请求的总时间,并不知道中继请求和数据库操作的时间占比。

如果做个粗略估计的话,观察第响应时间的中位数,在测试刚刚开始的时候是 2s ,之后略低于 3s 。猜测当数据库 IO 达到瓶颈的时候,平均要多花接近 1s 等待。

要验证这个猜想,简单的方式是调整 BATCH_UPDATE_ENABLED 启用批量更新,单请求的数据库写入会降至 1 次。或者再提高 SQL_MAX_OPEN_CONNS 的数值。同时可以查看本地日志和 sql 服务器的日志,辅助确认 sql 服务器不是瓶颈。

假如上面的方法无效,那就要考虑 pprof 之类的方式来定位瓶颈了。
信息有点少,最好能把部署环境、压测方式都贴上来,这样便于分析。

主要是 4c8G -> 6c8G 就能提升并发,这个现象很难解释。表面上只能判定说,内存不是瓶颈,与 GC 或者内存泄漏无关。

而且 2000/5000 rpm 的尺度是分钟,每秒也就是 30~80 的水平。这个数值过于低了,一般是业务逻辑造成的,而不是技术栈造成的。

另外关于 #19 讨论的锁,相关代码是 oneapi 向外发起链接的逻辑。虽然 net/transport 内部维护的连接池确实是有互斥锁的,但这里 race condition 非常弱。最差的情况等价于是退化到 openai 服务器不支持 keepalive ,每次都需要建立连接。而且每秒不到 100 的并发,基本不可能触发死锁。
我大概试了一下网站,没感觉查询有什么特别慢的地方。如果要定位慢查询的问题,最好贴上语句和配置信息什么的看一下。

在不改变技术框架的情况下,优化方案就是加索引。再具体一点,这个索引是要能通过用户输入的关键词,快速定位到特定字段包含关键词的数据集。

这里涉及到两个主要的技术点,一个是对文本字段进行分词,提取出关键词。另一个是通过关键词反查数据集,这个要构建的索引叫倒排索引( inverted index )。剩下的事情就是更改查询逻辑,前端查询请求走索引,后端直接取索引筛选出的数据集。(这个索引可以做到后端应用程序里,也可以保存到数据库里)

考虑到楼主非专业开发,我是建议手动做上面的事情。用其他的成熟方案,需要学习的东西其实更多,原理理解不到位的话很可能达不到效果,而且没有必要。


如果是我来做这个事情的话,可能会采取完全不同的方案,楼主可以参考一下。

因为这个数据库基本上是只读不写的,写场景基本只发生在数据修正或者批量更新的时候。所以优化的核心思路就是数据集(数据库)为查询优化。

虽然楼主没有透露这个数据库的表结构,考虑到万智牌大概就几万张,而数据集大概有 10 万条,这个数据库应该是冗余很低,符合范式的设计方式。

这一类数据库是为写操作和存储优化的,冗余很低,提取数据需要对数据表做组合查询。为读操作和查询优化的数据库正好相反,冗余很高。既然最终查询结果是卡牌集合,那就以某种数据结构来记录每张卡牌包含的所有信息,一次读取即可获得完整数据,省去了数据组合的过程。

即便有大量的冗余,内存中以这种数据结构存储卡牌信息,可能不会超过 4GB ,即使再翻个倍也不是问题。那对于查询系统来说,有没有 sql 数据库都不重要了。

当然 sql 对于数据录入还是很有意义的,一方面可能本身数据来源导入比较方便,另一方面修改低冗余数据远比高冗余数据方便。所以这里需要编写个脚本,在每次 sql 数据库更新之后,重新生成一份通用格式的(比如 csv 或者 json )的数据集,后端应用程序反序列化之后即可获得上述的内存数据库。

上面这些如果用技术语言描述,就是 sql 转 kv/nosql ,没有多少技术难度,就是个经验问题。

剩下的事情都差不多,分词、建索引。主要是一些经验层面的优化,这里就随便列举几个。

比如请求是可以缓存的,一般用在高频查询和分页上。再比如具体到这个查询场景说,一个请求如果需要返回几百个数据,要么不合理要么没必要。(人一般不需要这么多,返回太多都是在给爬虫服务了。)
楼主这个是非常简单的 cwd 的问题。

也许是现在大家写代码都过于依赖自动化工具,基础的东西反倒不知道了。不是说楼主,这种情况非常常见。我举几个例子:

- 即便大厂的项目,搞不清外部依赖的比比皆是,特别是 python 类的机器学习项目,鲜有能做好工程化的

- 各种所谓的跨平台工具,处理不好路径的正反斜杠,即使用心处理了,很大概率也是硬编码而不是用系统级的 path 方法

- 外表绚丽的 i18n 结果内部实现竟然不是 rune

反正大家都是草台班子……
1  2  3  4  5  6  7  8  9  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5039 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 35ms · UTC 07:42 · PVG 15:42 · LAX 00:42 · JFK 03:42
Developed with CodeLauncher
♥ Do have faith in what you're doing.