如何在D语言中从字符串中获取一个plain char*?

12

我正在尝试从D字符串(immutable(char)[])获取一个普通的、可变的C字符串(char*),以便将字符数据传递给旧的C代码。但是toStringz不起作用,因为我得到一个错误,说我"不能隐式转换表达式(toStringz(this.fileName()))的类型为immutable(char)*到char*"。我需要重新创建一个新的可变char数组并复制字符吗?

5个回答

13

如果您可以更改那个遗留C代码的D接口头文件,并且您确定遗留的C代码不会修改字符串,那么您可以使其接受一个const(char)*,例如:

char* strncpy(char* dest, const(char)* src, size_t count);
//                        ^^^^^^^^^^^^

我真的很困惑为什么你的答案有这么多赞...难道楼主没有提到他想要一个可变字符串吗? - user541686
@Mehrdad,这篇文章得到了赞同,因为OP可能不需要一个可变字符串。不过最好还是包括如何获取可变字符串的方法。 - he_the_great

9

是的,这并不美观,因为结果是不可变的。

这就是为什么我总是在我的代码中返回新数组的可变副本。没有必要使它们不可变。

解决方案:

你可以只做以下操作

char[] buffer = (myString ~ '\0').dup; //Concatenate a null terminator, then dup

然后使用buffer.ptr作为指针。

然而:

这样会浪费一个字符串。更好的方法可能是:

char[] buffer = myString.dup;
buffer ~= '\0'; //Hopefully this doesn't reallocate

而后可以使用buffer.ptr


另一种解决方案是使用如下方法:

char* toStringz(in char[] s)
{
    string result;
    if (s.length > 0 && s[$ - 1] == '\0') //Is it null-terminated?
    { result = s.dup; }
    else { result = new char[s.length + 1]; result[0 .. s.length][] = s[]; }
    return result.ptr;
}

这是最高效但也是最长的一种方法。
(编辑:哎呀,我的 if 里有个错别字;已经修复。)

先复制字符串,然后再附加\0,这样就不会出现两次重新分配内存(一次是为了空串连接,一次是为了复制)。因此,执行char[] buffer = (myString.dup ~ '\0')会更好。 - ratchet freak
@ratchetfreak: 不过它仍然会重新分配两次。 - user541686
如果编译器可以进行优化,它就不会这样做。但是 char[] buffer = (myString[] ~ '\0') 只会重新分配一次内存。 - ratchet freak
@ratchet:是的,但你不能依赖编译器来优化它。 - user541686
这样做是可行的,但如果接收代码实际上不需要改变任何东西,那么这不是一个好主意。 - BCS

4
如果你想把一个可变的char*传递给C函数,你需要分配一个可变的char[]。因为string是不可变的(char[]),所以它不起作用。你不能修改不可变变量,所以没有办法将需要改变其元素的string传递给一个函数(无论是C还是其他语言)。
所以,如果你有一个string,并且你需要将它传递给一个需要char[]的函数,那么你可以使用to!(char[])dup来获取它的可变副本。另外,如果你想将它传递给C函数,你需要在末尾添加一个'\0',使其成为零结尾字符串。最简单的方法是在char[]上执行~= '\0',但更有效的方法可能是这样做:
auto cstring = new char[](str.length + 1);
cstring[0 .. str.length] = str[];
cstring[$ - 1] = '\0';

无论哪种情况,你都需要将 cstring.ptr 传递给你调用的 C 函数。
如果你知道这个 C 函数不会改变字符串,那么你可以按照KennyTM 的建议,将 D 中的 C 函数签名改为 const(char)*,或者你可以将字符串强制转换。例如:
auto cstring = toStringz(str);
cfunc(cast(char*)cstring.ptr);

更改C函数的签名可能更加正确且不容易出错。听起来我们可能会更改std.conv.to,使其能够智能地将字符串转换为零终止字符串,当被强制转换为char*、const(char)*等类型时。因此,一旦完成这个操作,获取一个以零结尾的可变字符串应该更容易,但目前为止,你基本上只需要复制字符串并在末尾添加一个'\0',以使其成为零结尾字符串。但无论如何,你永远无法将string传递给需要修改它的C函数,因为string不能被改变。

1

没有上下文的情况下很难确定正确的解决方案是什么。

通常,如果C函数想要修改或写入字符串,它可能希望您提供缓冲区和长度。通常我会这样做:

分配一个缓冲区:

auto buffer = new char[](256);  // your own length instead of 256 here

然后调用C函数:

CWriteString(buffer.ptr, buffer.length);

-3
你可以尝试以下代码:
char a[]="abc";
char *p=a;
现在你可以将指针'p'传递到任何函数中的该数组。
希望它能够正常工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接