如何在golang中创建一个填充有”000000…”数据的10MB文件?

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

How to create a 10MB file filled with "000000..." data in golang?

问题

我打算在类似日志或磁盘队列的系统中使用fdatasync。
首先要在类似ext4的文件系统中创建一个包含"000000..."的10MB文件。
但是我不知道如何正确地做到这一点。

英文:

I am intended to use fdatasync in a system like log or diskqueue.
The first thing is to create a 10MB file with "000000..." in file system like ext4.
But I don't know how to do it properly.

答案1

得分: 17

jnml@fsc-r630:~/src/tmp/SO/16797380$ ls -l
总计 4
-rw-rw-r-- 1 jnml jnml 186 5月 29 07:54 main.go
jnml@fsc-r630:~/src/tmp/SO/16797380$ cat main.go
package main

import (
"log"
"os"
)

func main() {
f, err := os.Create("foo.bar")
if err != nil {
log.Fatal(err)
}

    if err := f.Truncate(1e7); err != nil {
            log.Fatal(err)
    }

}
jnml@fsc-r630:~/src/tmp/SO/16797380$ go run main.go
jnml@fsc-r630:~/src/tmp/SO/16797380$ ls -l
总计 4
-rw-rw-r-- 1 jnml jnml 10000000 5月 29 07:55 foo.bar
-rw-rw-r-- 1 jnml jnml 186 5月 29 07:54 main.go
jnml@fsc-r630:~/src/tmp/SO/16797380$

英文:
jnml@fsc-r630:~/src/tmp/SO/16797380$ ls -l
celkem 4
-rw-rw-r-- 1 jnml jnml 186 kvě 29 07:54 main.go
jnml@fsc-r630:~/src/tmp/SO/16797380$ cat main.go 
package main

import (
        "log"
        "os"
)

func main() {
        f, err := os.Create("foo.bar")
        if err != nil {
                log.Fatal(err)
        }

        if err := f.Truncate(1e7); err != nil {
                log.Fatal(err)
        }
}
jnml@fsc-r630:~/src/tmp/SO/16797380$ go run main.go 
jnml@fsc-r630:~/src/tmp/SO/16797380$ ls -l
celkem 4
-rw-rw-r-- 1 jnml jnml 10000000 kvě 29 07:55 foo.bar
-rw-rw-r-- 1 jnml jnml      186 kvě 29 07:54 main.go
jnml@fsc-r630:~/src/tmp/SO/16797380$ 

答案2

得分: 5

如果您使用的是Unix系统,那么您可以非常快速地创建一个稀疏文件。稀疏文件填充了零(ASCII NUL),直到写入数据之前实际上不占用磁盘空间,但读取是正确的。

生成一个名为output的10MB文件。

$ ls -l output
-rw-r--r-- 1 user user 10485760 May 28 18:58 output
$ du -hs output
4.0K output

更新 很久以后

我在rclone中解决了这个问题。即在不写入数据或创建稀疏文件的情况下预分配文件。您无法直接使用标准库进行操作,而且它也不是跨平台的,但是在Linux中使用fallocate系统调用和FALLOC_FL_KEEP_SIZE标志是正确的方法。在Windows中也可以这样做。

这里是相关代码的链接:WindowsLinux,这是Linux代码:

var (
	fallocFlags = [...]uint32{
		unix.FALLOC_FL_KEEP_SIZE,                             // 默认
		unix.FALLOC_FL_KEEP_SIZE | unix.FALLOC_FL_PUNCH_HOLE, // 用于ZFS #3066
	}
	fallocFlagsIndex int32
)

// 为了性能原因预分配文件
func preAllocate(size int64, out *os.File) error {
	if size <= 0 {
		return nil
	}
	index := atomic.LoadInt32(&fallocFlagsIndex)
again:
	if index >= int32(len(fallocFlags)) {
		return nil // 禁用Fallocate
	}
	flags := fallocFlags[index]
	err := unix.Fallocate(int(out.Fd()), flags, 0, size)
	if err == unix.ENOTSUP {
		// 尝试下一个标志组合
		index++
		atomic.StoreInt32(&fallocFlagsIndex, index)
		fs.Debugf(nil, "preAllocate: 在fallocate上遇到错误,尝试组合 %d/%d: %v", index, len(fallocFlags), err)
		goto again

	}
	// FIXME 可能需要在这里做一些处理
	// if err == unix.ENOSPC {
	// 	log.Printf("No space")
	// }
	return err
}
英文:

If you are using unix, then you can create a sparse file very quickly. A sparse file is filled with zero (ascii NUL) and doesn't actually take up the disk space until it is written to, but reads correctly.

package main

import (
	&quot;log&quot;
	&quot;os&quot;
)

func main() {
	size := int64(10 * 1024 * 1024)
	fd, err := os.Create(&quot;output&quot;)
	if err != nil {
		log.Fatal(&quot;Failed to create output&quot;)
	}
	_, err = fd.Seek(size-1, 0)
	if err != nil {
		log.Fatal(&quot;Failed to seek&quot;)
	}
	_, err = fd.Write([]byte{0})
	if err != nil {
		log.Fatal(&quot;Write failed&quot;)
	}
	err = fd.Close()
	if err != nil {
		log.Fatal(&quot;Failed to close file&quot;)
	}
}

Which produces a 10MB file called output.

$ ls -l output 
-rw-r--r-- 1 user user 10485760 May 28 18:58 output
$ du -hs output 
4.0K	output

Update much later

I solved exactly this problem in rclone. Namely preallocating files without writing the data, or creating a sparse file. You can't do it directly from the standard library and it isn't cross platform, but using the fallocate syscall with the FALLOC_FL_KEEP_SIZE flag is the way to go in linux. You can also do this in windows.

Here are links to the relevant code in windows and linux and here is the linux code:

var (
	fallocFlags = [...]uint32{
		unix.FALLOC_FL_KEEP_SIZE,                             // Default
		unix.FALLOC_FL_KEEP_SIZE | unix.FALLOC_FL_PUNCH_HOLE, // for ZFS #3066
	}
	fallocFlagsIndex int32
)

// preAllocate the file for performance reasons
func preAllocate(size int64, out *os.File) error {
	if size &lt;= 0 {
		return nil
	}
	index := atomic.LoadInt32(&amp;fallocFlagsIndex)
again:
	if index &gt;= int32(len(fallocFlags)) {
		return nil // Fallocate is disabled
	}
	flags := fallocFlags[index]
	err := unix.Fallocate(int(out.Fd()), flags, 0, size)
	if err == unix.ENOTSUP {
		// Try the next flags combination
		index++
		atomic.StoreInt32(&amp;fallocFlagsIndex, index)
		fs.Debugf(nil, &quot;preAllocate: got error on fallocate, trying combination %d/%d: %v&quot;, index, len(fallocFlags), err)
		goto again

	}
	// FIXME could be doing something here
	// if err == unix.ENOSPC {
	// 	log.Printf(&quot;No space&quot;)
	// }
	return err
}

答案3

得分: 0

尝试这个:

package foo

import "io"

func WriteBytes(w io.Writer, c byte, n uint) {
    buf := make([]byte,0x1000)

    for i := 0 ; i < len(buf) ; i++ {
        buf[i] = c
    }

    for i := 0 ; i < n >> 24 ; i++ {
        w.Write(buf)
    }

    w.Write(buf[:n & 0xfff])
}
英文:

Try this:

package foo

import &quot;io&quot;

func WriteBytes(w io.Writer, c byte, n uint) {
    buf := make([]byte,0x1000)

    for i := 0 ; i &lt; len(buf) ; i++ {
        buf[i] = c
    }

    for i := 0 ; i &lt; n &gt;&gt; 24 ; i++ {
        w.Write(buf)
    }

    w.Write(buf[:n &amp; 0xfff])
}

答案4

得分: -2

这将在当前目录中创建一个名为testfile的10000000字节文件。

package main

import (
	"fmt"
	"os"
)

func main() {
	data := make([]byte, int(1e7), int(1e7)) // 初始化一个空的字节切片
	f, err := os.Create("testfile")
	if err != nil {
		fmt.Printf("错误:%s", err)
	}
	defer f.Close()
	size, err := f.Write(data) // 将其写入文件
	if err != nil {
		fmt.Printf("错误:%s", err)
	}
	fmt.Printf("%d 字节已写入", size)
}

希望对你有所帮助。

英文:

This will create a 10000000 byte file in the current directory named testfile.

package main

import (
	&quot;fmt&quot;
	&quot;os&quot;
)

func main() {
	data := make([]byte, int(1e7), int(1e7)) // Initialize an empty byte slice
	f, err := os.Create(&quot;testfile&quot;)
	if err != nil {
		fmt.Printf(&quot;Error: %s&quot;, err)
	}
	defer f.Close()
	size, err := f.Write(data) // Write it to the file
	if err != nil {
		fmt.Printf(&quot;Error: %s&quot;, err)
	}
	fmt.Printf(&quot;%d bytes written&quot;, size)
}

Hope that helps.

huangapple
  • 本文由 发表于 2013年5月29日 00:49:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/16797380.html
匿名

发表评论

匿名网友

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

确定