go 实现日志采集-01-windows10 install go 并初步实现基本的文件监听功能
download
下载 https://go.dev/dl/go1.21.3.windows-amd64.msi
双击安装。
测试验证
>go version
go version go1.21.3 windows/amd64
goland 只能免费使用 30 天,有免费的 go 开发工具吗
是的,Go语言有一些免费的开发工具可供使用。以下是一些常用的免费Go开发工具:
Visual Studio Code(VS Code):这是一款免费的轻量级代码编辑器,支持多种编程语言,包括Go语言。它具有丰富的插件生态系统,可以通过安装Go插件来提供对Go语言的支持。
LiteIDE:这是一款专为Go语言开发设计的开源集成开发环境(IDE)。它提供了一系列功能,如代码补全、调试器、项目管理等,可以帮助你更高效地开发Go应用程序。
Sublime Text:这是一款流行的文本编辑器,也可以用于Go语言开发。虽然它的免费版本有限制,但你可以购买许可证解锁更多功能。
Atom:这是一款免费的开源文本编辑器,支持多种编程语言,包括Go语言。它具有丰富的插件和主题,可以根据个人喜好进行定制。
这些免费的Go开发工具都提供了基本的代码编辑和调试功能,可以满足大多数Go开发者的需求。你可以根据个人偏好选择适合自己的工具进行Go语言开发。
如何通过 go 实现一个监听指定文件夹文件变化,并获取变化内容的程序?类似于 filebeta
1. 安装依赖包
go get github.com/fsnotify/fsnotify
报错1
$ go get github.com/fsnotify/fsnotify
go: go.mod file not found in current directory or any parent directory.
'go get' is no longer supported outside a module.
To build and install a command, use 'go install' with a version,
like 'go install example.com/cmd@latest'
For more information, see https://golang.org/doc/go-get-install-deprecation
or run 'go help get' or 'go help install'.
原因:
如果你在使用 Go 1.11 或更高版本,并且没有使用 Go Modules,那么你可能会遇到 "go get" 不再支持的问题。在这种情况下,你可以尝试以下解决方法:
进入你的项目目录:在终端中,使用
cd /your/project/directory
命令进入你的项目目录。初始化 Go Modules:运行
go mod init
命令来初始化 Go Modules。这将创建一个 go.mod 文件。使用 Go Modules 来获取包:使用
go get
命令获取你所需的包,例如go get github.com/fsnotify/fsnotify
。更新你的代码:在你的代码中,使用
import
语句导入所需的包。
这样,你就可以在 Go Modules 环境中解决 "go get" 不再支持的问题。希望这可以帮到你!如有任何疑问,请随时提问。
报错 2
$ go mod init
go: cannot determine module path for source directory D:\gocode\logcollect (outside GOPATH, module path must be specified)
Example usage:
'go mod init example.com/m' to initialize a v0 or v1 module
'go mod init example.com/m/v2' to initialize a v2 module
Run 'go help mod init' for more information.
这个错误信息是由于在 Go Modules 环境中,无法确定模块的路径。这通常发生在你的代码目录不在 GOPATH 中时。
为了解决这个问题,你可以按照错误信息中提供的示例使用 go mod init
命令来初始化一个模块。你可以根据你的实际情况选择不同的模块路径。
例如,如果你的代码目录是 D:\gocode\logcollect
,你可以运行以下命令来初始化一个 v0 或 v1 模块:
go mod init example.com/m
或者,如果你想初始化一个 v2 模块,你可以运行以下命令:
go mod init example.com/m/v2
这样,你就可以为你的代码创建一个模块,并解决这个错误。希望这可以帮到你!如有任何疑问,请随时提问。
v0 或 v1 模块,和 v2 模块有什么区别?作用是什么
在 Go Modules 中,模块版本通常遵循语义化版本控制(Semantic Versioning)规范。
v0 或 v1 模块是指在初始化模块时使用的版本,而 v2 模块是指在初始化模块时使用的 v2 版本。
v0 或 v1 模块:
- 这些模块是在 Go Modules 刚开始引入时创建的。
- 它们使用的是 v0 或 v1 的版本控制规范。
- 当你使用
go mod init example.com/m
初始化模块时,默认创建的是 v0 或 v1 模块。 - 这些模块的版本号通常遵循
vX.Y.Z
的格式,其中 X、Y 和 Z 分别表示主版本号、次版本号和修订号。
v2 模块:
- v2 模块是在 v0 或 v1 模块的基础上进行的重大修改或重构。
- 它们使用的是 v2 版本控制规范。
- 当你使用
go mod init example.com/m/v2
初始化模块时,创建的是 v2 模块。 - v2 模块的版本号通常遵循
v2.Y.Z
的格式,其中 Y 和 Z 分别表示次版本号和修订号。
区别:
- v0 或 v1 模块和 v2 模块在版本控制规范和版本号的格式上有所不同。
- v2 模块通常用于在 v0 或 v1 模块的基础上进行重大修改或重构,以避免破坏现有的依赖关系。
模块的作用:
- 模块是 Go Modules 的核心概念,用于管理代码的依赖关系和版本控制。
- 它们允许你明确指定你的代码所依赖的其他模块,并确保这些依赖关系的版本是可控的。
- 模块还可以帮助你更好地管理和共享你的代码,使其更易于维护和部署。
希望这可以解答你的问题!如果还有其他疑问,请随时提问。
我们根据提示,输入:
go mod init example.com/m
日志如下:
$ go mod init example.com/m
go: creating new go.mod: module example.com/m
go: to add module requirements and sums:
go mod tidy
2. 继续执行安装
然后继续安装 fsnotify 依赖
go get github.com/fsnotify/fsnotify
这个命令的含义
go get github.com/fsnotify/fsnotify
是一个用于获取第三方包或库的 Go 命令。
具体来说,这个命令的作用是从远程代码仓库(例如 GitHub)中下载并安装 github.com/fsnotify/fsnotify
这个包。
该包是一个用于监听文件系统事件的 Go 包,可以用于监视文件或文件夹的变化。
执行 go get
命令后,Go 工具链会自动下载指定包的源代码,并将其安装到你的 GOPATH 或 Go Modules 中,使你可以在自己的项目中导入和使用该包。
例如,在你的 Go 代码中,你可以使用以下方式导入 github.com/fsnotify/fsnotify
包:
import "github.com/fsnotify/fsnotify"
然后,你就可以使用该包提供的功能来监听文件系统事件,如文件的创建、修改、删除等。
总结一下, go get
命令的作用是方便地获取和安装第三方包或库,使你可以在自己的项目中使用这些包来扩展功能。
安装报错
$ go get github.com/fsnotify/fsnotify
go: module github.com/fsnotify/fsnotify: Get "https://proxy.golang.org/github.com/fsnotify/fsnotify/@v/list": dial tcp 142.251.43.17:443: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.
错误原因:
具体原因:默认go的代理网站是 GOPROXY=https://proxy.golang.org,direct
,是一个外网地址,国内访问不到,因此我们需要修改代理网站。
解决:使用命令
go env -w GOPROXY=https://goproxy.cn,direct
更改代理网站,然后再重新执行go get命令即可成功download了。
成功的安装日志如下:
$ go get github.com/fsnotify/fsnotify
go: downloading github.com/fsnotify/fsnotify v1.7.0
go: downloading golang.org/x/sys v0.4.0
go: added github.com/fsnotify/fsnotify v1.7.0
go: added golang.org/x/sys v0.4.0
编写代码
要通过Go实现一个监听指定文件夹文件变化并获取变化内容的程序,你可以使用 fsnotify
包来实现。下面是一个示例代码:
package main
import (
"log"
"os"
"path/filepath"
"github.com/fsnotify/fsnotify"
)
func main() {
// 创建一个新的文件系统监视器
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()
// 指定要监听的文件夹路径
dirPath := "D:\\logsdata"
// 使用 filepath 包中的 Walk 函数,遍历文件夹中的所有文件,并将它们添加到监视器中
err = filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
err = watcher.Add(path)
if err != nil {
log.Println(err)
}
}
return nil
})
if err != nil {
log.Fatal(err)
}
// 启动一个无限循环来监听文件事件
for {
select {
case event, ok := 500*time.Millisecond {
processEvent(event)
lastWriteTime = time.Now()
}
case err, ok := <-watcher.Errors:
if !ok {
return
}
log.Println("错误:", err)
}
}
}
func processEvent(event fsnotify.Event) {
fmt.Println("处理事件:", event)
// 在这里添加你希望执行的处理逻辑
}
在这个示例代码中,我们使用一个变量 lastWriteTime
来存储最后一次写入事件的时间戳。
当收到写入事件时,我们首先检查距离上次写入事件的时间间隔是否超过了500毫秒,如果是,则处理事件并更新 lastWriteTime
。
这种方式更加简单,通过时间间隔来判断重复事件,避免了使用定时器的复杂性。
希望这个示例代码能满足你的需求!如果还有其他问题,请随时提问。
代码调整
我们结合上面的代码,决定使用 map 来保存每一个文件的最后修改时间。
map 的创建
go 如何创建一个 map 数据结构,key 是 string, value 是一个 long 时间戳。
在 Go 中,你可以使用内置的 map
数据结构来创建一个映射,其中 key 是字符串类型,value 是一个长整型(时间戳)。
以下是一个示例代码,演示了如何创建和使用这样的 map
数据结构:
package main
import (
"fmt"
"time"
)
func main() {
// 创建一个 map,key 是字符串,value 是长整型(时间戳)
timestamps := make(map[string]int64)
// 添加键值对到 map 中
timestamps["key1"] = time.Now().Unix()
timestamps["key2"] = time.Now().Unix()
// 读取 map 中的值
value1 := timestamps["key1"]
value2 := timestamps["key2"]
// 打印值
fmt.Println("key1 对应的时间戳:", value1)
fmt.Println("key2 对应的时间戳:", value2)
}
在上面的示例代码中,我们使用 make
函数创建了一个 map
数据结构,其中 key 是字符串类型,value 是长整型(时间戳)。
然后,我们通过使用键来给 map
赋值,并使用键来获取相应的值。
代码调整
我们通过一个 map 记录每一个文件和对应的修改时间。
然后计算差值,小于 50ms 则不做内容变化输出:
//create map save data
fileUpdateTimes := make(map[string]int64)
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
switch event.Op {
case fsnotify.Create:
fmt.Println("文件或文件夹被创建:", event.Name)
case fsnotify.Write:
fmt.Println("文件被写入:", event.Name)
// 存放修改的时间
lastTimeMs := fileUpdateTimes[event.Name];
currentTimeMs := getTimeMs();
if(lastTimeMs != 0) {
diffTimeMs := currentTimeMs - lastTimeMs;
fmt.Println("diffTimeMs:", diffTimeMs)
if(diffTimeMs <= 50) {
break;
}
}
// update lastTime
fmt.Println("update time:", currentTimeMs)
fileUpdateTimes[event.Name] = currentTimeMs;
// 获取文件内容
writeContent := getFileContent(event.Name);
fmt.Println("文件被写入后的内容:", writeContent)
case fsnotify.Remove:
fmt.Println("文件或文件夹被删除:", event.Name)
case fsnotify.Rename:
fmt.Println("文件或文件夹被重命名:", event.Name)
case fsnotify.Chmod:
fmt.Println("文件或文件夹权限被修改:", event.Name)
}
case err, ok := <-watcher.Errors:
if !ok {
return
}
log.Println("错误:", err)
}
}
func getTimeMs() int64 {
return time.Now().UnixNano() / int64(time.Millisecond);
}
测试
我们再次测试文件的内容变更,日志如下:
$ ./m.exe
2023/11/01 23:42:29 开始处理文件夹: D:\logsdata
文件被写入: D:\logsdata\2.txt
update time: 1698853354304
文件被写入后的内容: 11111哈哈哈哈顶顶顶顶对对对对对dddddddddddddddddd
文件被写入: D:\logsdata\2.txt
diffTimeMs: 1
文件被写入: D:\logsdata\2.txt
diffTimeMs: 1
文件被写入: D:\logsdata\2.txt
diffTimeMs: 1
文件被写入: D:\logsdata\2.txt
diffTimeMs: 8146
update time: 1698853362450
文件被写入后的内容: 11111哈哈哈哈顶顶顶顶对对对对对dddddddddddddddddddddd
文件被写入: D:\logsdata\2.txt
diffTimeMs: 1
文件被写入: D:\logsdata\2.txt
diffTimeMs: 1
文件被写入: D:\logsdata\2.txt
diffTimeMs: 1
文件被写入: D:\logsdata\2.txt
diffTimeMs: 8180
update time: 1698853370630
文件被写入后的内容: 11111哈哈哈哈顶
可以看到触发了多次文件的写入动作,但是只有超过一定的时间,我们才输出对应的文件内容。
v6-文件编码问题
说明
默认我们的方法是 utf-8 的,如果我们是其他的编码怎么办?
比如我们创建一个 gb2313 的文件。
输入内容:
你好啊,我的老伙计。
这时候输出是:
文件被写入后的内容: ▒▒ð▒▒▒▒ҵ▒▒ϻ▒ơ▒
指定编码读取内容
chat-如何指定编码?
导入包
"github.com/axgle/mahonia"
方法调整
//默认以 UTF-8 的编码读取文件内容
func getFileContent(filePath string) string {
// 默认编码
content, err := ioutil.ReadFile(filePath)
if err != nil {
fmt.Println("无法读取文件:", err)
return "";
}
text := string(content)
// 指定其他编码, 转 GBK
if(filePath == "D:\\logsdata\\gbk.txt") {
text = mahonia.NewDecoder("gbk").ConvertString(text)
}
return text;
}
install
go get github.com/axgle/mahonia
这个时候在测试 GBK 文件,编码如下:
文件被写入: D:\logsdata\gbk.txt
update time: 1698855304939
文件被写入后的内容: 你好啊,我的老伙计。
ddd
参考资料
chat
https://www.codingdict.com/questions/63128
https://blog.csdn.net/weixin_33915554/article/details/91869462
https://juejin.cn/s/golang 读取文件乱码
https://blog.csdn.net/kkx12138/article/details/87927175