Golang中cmd/cgo无法使用cgo函数创建文件:文件名编码无效问题

Golang中cmd/cgo无法使用cgo函数创建文件:文件名编码无效问题 请先回答以下问题再提交问题。谢谢!

你使用的 Go 版本是什么(go version)?

go1.10.2 linux/amd64

这个问题在最新版本中是否仍然存在?

不确定,没有在我现有版本之上的版本中测试过

你使用的操作系统和处理器架构是什么(go env)?

GOHOSTARCH=“amd64” GOHOSTOS=“linux”

你做了什么?

我尝试将一个 Go 程序转换为共享库,以便在 R 脚本中使用。最初我用一个简单的功能进行测试:一个接收输入路径(字符串)并在磁盘上创建文件的函数。我的问题是,包含路径的字符串变成了某种无效编码的内容,如下所示:

如果可能,请提供重现错误的步骤。 一个完整的可运行程序更好。 在 play.golang.org 上的链接最佳。

这是我的函数:

import "C"

(...)

//export testWriter
func testWriter(path string) string {

	file, err := os.Create(path)
	if err != nil {
		return "error"
	}
	defer file.Close()

	return path
}

我用以下命令编译库:

go build -o gowriter.so -buildmode=c-shared ./gowriter

然后我这样运行我的 R 脚本:

 Rscript -e 'dyn.load("/home/user/go/src/github.com/user/gowriter.so"); .Call("testWriter", "/home/user/test.txt")'

我还尝试在 Python 程序中运行:

from ctypes import *
lib = cdll.LoadLibrary("/home/user/go/src/github.com/prvst/testwriter.so")
lib.testReader("/home/user/test.txt")

我得到的是一个名为 �(无效编码) 的文件。这字面意思就是文件名,包括你看到的那里的感叹号符号。

我添加了一些打印函数来尝试理解问题,这是我得到的结果:

fmt.Println(reflect.TypeOf(path))                     # string
fmt.Println(reflect.TypeOf(C.CString(path)))   # *main._Ctype_char
fmt.Println(C.CString(path))                            # 0x559b609e1210

我还尝试了这个但没有成功:

func testWriter(path *C.char) *C.char {

	file, err := os.Create(C.GoString(path))
	if err != nil {
		return C.CString("error")
	}
	defer file.Close()

	return path
}

我觉得奇怪的是,函数在返回语句处结束,我可以在终端上看到传递给函数的正确路径字符串。

你期望看到什么?

在我的磁盘上看到一个名为 test.txt 的空文件

你实际看到了什么?

在我的磁盘上看到一个名为 �(无效编码) 的空文件


更多关于Golang中cmd/cgo无法使用cgo函数创建文件:文件名编码无效问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

我虽然没有这方面的具体经验,但这听起来像是源于Go语言的UTF-8编码标准。或许可以使用utf8 C语言库来解析?

// 代码示例
func main() {
    fmt.Println("hello world")
}

更多关于Golang中cmd/cgo无法使用cgo函数创建文件:文件名编码无效问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


根据你的描述,问题出现在Go与C之间字符串编码的转换上。当Go字符串通过cgo传递给C时,需要使用正确的转换函数。你的第二个尝试方向是正确的,但需要确保字符串在Go和C之间正确传递。

以下是修复后的代码:

package main

/*
#include <stdlib.h>
*/
import "C"
import (
    "os"
    "unsafe"
)

//export testWriter
func testWriter(path *C.char) *C.char {
    // 将C字符串转换为Go字符串
    goPath := C.GoString(path)
    
    file, err := os.Create(goPath)
    if err != nil {
        // 返回错误信息,需要分配新的C字符串
        errMsg := C.CString("error: " + err.Error())
        return errMsg
    }
    defer file.Close()
    
    // 返回原始路径,不需要重新分配
    return path
}

func main() {
    // 空main函数,构建共享库时需要
}

关键点说明:

  1. 函数签名必须使用C类型:*C.char 而不是Go的 string
  2. 使用 C.GoString() 将C字符串转换为Go字符串
  3. 返回字符串时,如果返回新的字符串内容,需要使用 C.CString() 分配内存
  4. 如果返回输入的原始路径,可以直接返回 path 参数

编译命令:

go build -o gowriter.so -buildmode=c-shared ./gowriter

在R中的调用方式:

dyn.load("/home/user/go/src/github.com/user/gowriter.so")
result <- .Call("testWriter", "/home/user/test.txt")

在Python中的调用方式:

from ctypes import *
lib = cdll.LoadLibrary("/home/user/go/src/github.com/prvst/gowriter.so")
lib.testWriter.argtypes = [c_char_p]
lib.testWriter.restype = c_char_p

result = lib.testWriter(b"/home/user/test.txt")
print(result.decode('utf-8'))

注意在Python中需要正确设置参数类型和返回类型,并使用字节字符串(b"string")传递路径。

这个修复应该能解决文件名编码无效的问题,正确创建名为"test.txt"的文件。

回到顶部