在Go语言中使用`ioutil.ReadFile()`读取1200多个文件时出现错误。

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

Error reading 1200+ files with ioutil.ReadFile() in Go

问题

我正在尝试读取一个目录中的所有文件(超过10,000个),但是当我处理了大约1400个文件后,出现了一个"打开的文件太多"的错误。我已经显式调用了垃圾回收器,但对于我的问题似乎没有什么作用。我检查了ioutil包的源代码,发现ReadFile在内部使用了defer file.Close()(如预期所示)。那么这里出了什么问题呢?

const TEMPLATE = `{ "source_db" : "CDARCHIEF", "doc_type" : "%s", "referentie" : "%s", "bestandsnaam" : "%s", "tekst" : "%s" }`
const MAPPING = `{ ... }`

var DIR, DOCTYPE, URL string

func init() {
	flag.StringVar(&DIR, "d", "./", "de directory met de ge-ocrde bestanden")
	flag.StringVar(&DOCTYPE, "t", "AG", "document type [ AG, CO, NN ]")
	flag.StringVar(&URL, "url", "...", "url voor de juiste index")
}

func main() {
	flag.Parse()
	fmt.Println("CD Archive Importer")
	importDocuments()
}

func importDocuments() {
	logfile, _ := os.Create("./importer.log")
	defer logfile.Close()

	files, _ := ioutil.ReadDir(DIR)
	error_counter := 0

	for i, file := range files {
		if math.Mod(float64(i), 400.0) == 0.0 {
			runtime.GC()
			fmt.Println("Running garbage collector")
		}

		fmt.Printf("Importing ( %d / %d ) [ errors: %d ]\r", i+1, len(files), error_counter)

		contents, err := ioutil.ReadFile(DIR + "/" + file.Name())
		if err != nil {
			error_counter = error_counter + 1
			logfile.WriteString(fmt.Sprintf("[ERROR/IO] : %s | %s\n", file.Name(), err))
			continue
		}

		contents_string := strings.Replace(string(contents), "\n", " ", -1)
		contents_string = strings.Replace(contents_string, "\"", " ", -1)
		contents_string = strings.Replace(contents_string, "\\\"", " ", -1)

		referentie := strings.Trim(file.Name(), ".txt")
		message := strings.NewReader(fmt.Sprintf(TEMPLATE, DOCTYPE, referentie, file.Name(), contents_string))

		resp, error := http.Post(URL, "application/json", message)
		if error != nil {
			error_counter = error_counter + 1
			logfile.WriteString(fmt.Sprintf("[ERROR/NET] : %s | %s | %s\n", file.Name(), resp.Status, error))
			continue
		}
		defer resp.Body.Close()

		if resp.StatusCode != 201 {
			body, _ := ioutil.ReadAll(resp.Body)
			error_counter = error_counter + 1
			logfile.WriteString(fmt.Sprintf("[ERROR/ES] : %s | %s | %s\n", file.Name(), resp.Status, string(body)))
		}

	}

	fmt.Println("\nDone!")
}

我知道大约两年前有一个类似的问题,但是那个问题对我的问题没有有用的答案。

英文:

I'm trying to read all the files (+10.000) in a directory, but when i've processed about 1400 files I get a 'too many open files' error. I've added an explicit call to the garbage collector, but that doesn't seem to do much for my problem. I checked the source for ioutil package and ReadFile uses defer file.Close() internally (as expected). So what is going wrong here?

    const TEMPLATE = `{ "source_db" : "CDARCHIEF", "doc_type" : "%s", "referentie" : "%s", "bestandsnaam" : "%s", "tekst" : "%s" }`
const MAPPING = `{ ... }`
var DIR, DOCTYPE, URL string
func init() {
flag.StringVar(&DIR, "d", "./", "de directory met de ge-ocrde bestanden")
flag.StringVar(&DOCTYPE, "t", "AG", "document type [ AG, CO, NN ]")
flag.StringVar(&URL, "url", "...", "url voor de juiste index")
}
func main() {
flag.Parse()
fmt.Println("CD Archive Importer")
importDocuments()
}
func importDocuments() {
logfile, _ := os.Create("./importer.log")
defer logfile.Close()
files, _ := ioutil.ReadDir(DIR)
error_counter := 0
for i, file := range files {
if math.Mod(float64(i), 400.0) == 0.0 {
runtime.GC()
fmt.Println("Running garbage collector")
}
fmt.Printf("Importing ( %d / %d ) [ errors: %d ]\r", i+1, len(files), error_counter)
contents, err := ioutil.ReadFile(DIR + "/" + file.Name())
if err != nil {
error_counter = error_counter + 1
logfile.WriteString(fmt.Sprintf("[ERROR/IO] : %s | %s\n", file.Name(), err))
continue
}
contents_string := strings.Replace(string(contents), "\n", " ", -1)
contents_string = strings.Replace(contents_string, "\"", " ", -1)
contents_string = strings.Replace(contents_string, "\\", " ", -1)
referentie := strings.Trim(file.Name(), ".txt")
message := strings.NewReader(fmt.Sprintf(TEMPLATE, DOCTYPE, referentie, file.Name(), contents_string))
resp, error := http.Post(URL, "application/json", message)
if error != nil {
error_counter = error_counter + 1
logfile.WriteString(fmt.Sprintf("[ERROR/NET] : %s | %s | %s\n", file.Name(), resp.Status, error))
continue
}
defer resp.Body.Close()
if resp.StatusCode != 201 {
body, _ := ioutil.ReadAll(resp.Body)
error_counter = error_counter + 1
logfile.WriteString(fmt.Sprintf("[ERROR/ES] : %s | %s | %s\n", file.Name(), resp.Status, string(body)))
}
}
fmt.Println("\nDone!")
}

I know that there is a somewhat similar question from about 2 years ago, but that didn't have a useful answer for my issue.

答案1

得分: 0

你可以考虑使用filepath.Walk。我已经成功地在10,000多个文件上使用它而没有出现问题。或者你可以深入源代码,看看他们在资源管理方面是否有任何不同的做法。

此外,这个for循环看起来有点复杂,你可以只使用整数和%运算符。

for i := 0; i < 1000000; i += 1 {
    if i % 5000 == 0 {
        fmt.Println(i)
    }
}
英文:

You may want to consider using filepath.Walk. I have successfully used it on 10k+ files without trouble. Alternatively you could dig into the source and see if they do anything different with regards to resource management.

Also the for loop seems a bit baroque, you can just use integers and the % operator.

for i := 0; i &lt; 1000000; i += 1 {
if i % 5000 == 0 {
fmt.Println(i)
}
}

huangapple
  • 本文由 发表于 2014年4月29日 18:54:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/23362632.html
匿名

发表评论

匿名网友

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

确定