英文:
FilePaths in Go
问题
这是Mark Summerfield的《Go语言编程》一书中的示例代码。
package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
var britishAmerican = "british-american.txt"
func init() {
dir, _ := filepath.Split(os.Args[0])
britishAmerican = filepath.Join(dir, britishAmerican)
}
func main() {
rawBytes, err := ioutil.ReadFile(britishAmerican)
if err != nil {
fmt.Println(err)
}
text := string(rawBytes)
usForBritish := make(map[string]string)
lines := strings.Split(text, "\n")
fmt.Println(lines)
for _, line := range lines {
fields := strings.Fields(line)
if len(fields) == 2 {
usForBritish[fields[0]] = fields[1]
}
}
fmt.Println(usForBritish)
}
当我将init()
函数注释掉后,代码可以正常运行。如果保留init()
函数,我会得到以下错误:
open /var/folders/l6/rdqtyrfd303dw1cz8qvlfcvc0000gn/T/go- build652175567/command-line-arguments/_obj/exe/british-american.txt: no such file or directory exit status 1
我的问题是,为什么init()
函数无法从适当的目录中获取文件?
英文:
So this is the example from Programming in Go by Mark Summerfield.
package main
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
var britishAmerican = "british-american.txt"
func init() {
dir, _ := filepath.Split(os.Args[0])
britishAmerican = filepath.Join(dir, britishAmerican)
}
func main() {
rawBytes, err := ioutil.ReadFile(britishAmerican)
if err != nil {
fmt.Println(err)
}
text := string(rawBytes)
usForBritish := make(map[string]string)
lines := strings.Split(text, "\n")
fmt.Println(lines)
for _, line := range lines {
fields := strings.Fields(line)
if len(fields) == 2 {
usForBritish[fields[0]] = fields[1]
}
}
fmt.Println(usForBritish)
}
When I run this code with the init() func commented out, it works perfectly fine. If I leave it in I get this error:
open /var/folders/l6/rdqtyrfd303dw1cz8qvlfcvc0000gn/T/go- build652175567/command-line-arguments/_obj/exe/british-american.txt: no such file or directory exit status 1
My question is, why does the init()
func not grab the file from the appropriate directory?
答案1
得分: 3
你在init函数中更改了变量britishAmerican
。没有init()
,程序会在当前目录中查找(没有给出路径,只有文件名)。有了init()
,它会在可执行文件所在的路径中查找(os.Args[0]
)。而使用go run main.go
时,可执行文件所在的目录不是当前工作目录。
你应该使用go build
来构建二进制文件,然后运行它,或者告诉我们你想要实现什么(如@RoninDev所写)。
我提到的最小可复现示例(MCVE)可能如下所示:
package main
import (
"io/ioutil"
"log"
"os"
"path/filepath"
)
var filename = "foo.txt"
func init() {
// 将下面的条件改为 true,会导致问题
if false {
dir, _ := filepath.Split(os.Args[0])
filename = filepath.Join(dir, filename)
}
}
func main() {
// 需要在当前目录中有一个名为 'foo.txt' 的文件
_, err := ioutil.ReadFile(filename)
if err != nil {
log.Fatal(err)
}
}
当然,这个示例可以更短,但这应该足够让社区中的其他人看出发生了什么。
英文:
You change the variable britishAmerican
in the init function. Without init()
, the program looks in the current directory (no path given, only the file name). With init()
, it looks in the path where the executable is (os.Args[0]
). And with go run main.go
, the directory with the executable is not the current working directory.
You should use go build
to build the binary and then run it, or you should tell us what you want to achieve (as written by @RoninDev).
The MCVE I've mentioned could look like this:
package main
import (
"io/ioutil"
"log"
"os"
"path/filepath"
)
var filename = "foo.txt"
func init() {
// change to true and things break
if false {
dir, _ := filepath.Split(os.Args[0])
filename = filepath.Join(dir, filename)
}
}
func main() {
// requires a file 'foo.txt' in the current directory
_, err := ioutil.ReadFile(filename)
if err != nil {
log.Fatal(err)
}
}
It can (of course) be even shorter, but this should be enough for the others in the community to see what is going on.
答案2
得分: 2
根据你提供的内容,我给你翻译如下:
在我看来,程序期望在可执行文件所在的目录中有一个名为 british-american.txt
的文件。
init()
函数的代码就是这样做的——它找到可执行文件的路径,并构建一个相对于该路径的字典路径。
从你的错误信息中我可以看出,你正在使用 go run
命令来运行代码。这会在 /tmp
目录中创建一个临时可执行文件并运行它。如果你保留 init()
代码,它将在 /tmp
目录中寻找字典文件,但找不到。如果你将 init()
代码移除,它将在当前目录中寻找字典文件,并且会成功。
如果你想按照作者的意图来使用,那么请使用 go build
命令来构建一个二进制文件,然后运行它——这样会起作用。
英文:
It looks to me like the program is expecting a file called british-american.txt
in the directory that the executable is in.
That is what the code in init()
does - it finds the path the the executable and constructs a path to the dictionary relative to that.
I can see from your error message that you are using go run
to run the code. This makes a temporary executable in /tmp
and runs that. If you leave the init()
code in then it will look for the dictionary in the /tmp
directory and it won't find it. If you take the init()
code out it will look for the dictionary in the current directory and it will succeed.
If you want to use it as the author intended then use go build
to build a binary and then run it - that will work.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论