Go中的文件路径

huangapple go评论76阅读模式
英文:

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.

huangapple
  • 本文由 发表于 2015年1月29日 15:19:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/28209006.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定