如何用String更新libc :: c_char数组?

时间:2015-03-26 12:10:05

标签: encoding rust ffi

我已经为C / C ++编写了一些FFI代码,

use libc::c_char;
use std::ffi::CString;

type arr_type = [c_char; 20]; // arr_type is the type in C
let mut arr : arr_type = [0; 20]; 

let s = "happy123";
let c_s = CString::new(s).unwrap();
let s_ptr = c_s.as_ptr();

如何使用字符串arr更新s?在C / C ++中,我可以使用memcpystrcpy等...

我尝试了很多,比如使用rlibc :: memcpy,发现它不能和libc一起使用..但编译器不允许我通过,关于Rust的数组信息非常少


添加: 阅读完回复后,我想添加一些信息和更多问题。

1

在C ++中,我使用strcpy_s将字符串复制到char数组,导致字符串的长度和数组的大小都是已知的。

我已尝试过以下两种方法。

std::iter::Zip方法,看起来非常像strcpy_s,但我不知道是否存在性能影响。

copy_nonoverlapping方法,它使用as_mut_ptr()将数组转换为指针然后没有长度信息,因为它在unsafe { }块中,我尝试复制一个字符串更长然后数组并没有错误显示......我想知道这是否正常?

在Rust中是否有类似strcpy_s的函数?

2

我使用windows和msvc,对于char数组,我的意思是not deal with encoding或使用default codepage encoding

下面的

在源文件中都可以:

C ++:

std::string s = "world is 世界";
std::wstring ws = L"world is 世界";

Qt的:

QString qs = QStringLiteral("world is 世界");

Python 3:

s = 'world is 世界'

但是在Rust,下面可能是错的?正如我在Eclipse Debug窗口中看到的那样。

let s = "world is 世界";

我发现了锈编码并尝试了这些:

use encoding::{Encoding, EncoderTrap};
use encoding::all::GB18030;

let s = "world is 世界";  
let enc = GB18030.encode(&s , EncoderTrap::Strict);

在Rust有没有更好的方法?

3 个答案:

答案 0 :(得分:2)

这是一个不需要不安全代码的解决方案,但不幸的是大多数方法都标记为不稳定。

#![feature(libc)]
#![feature(core)]
#![feature(collections)]

extern crate libc;

use libc::c_char;
use std::ffi::CString;
use std::slice::IntSliceExt;

type arr_type = [c_char; 20];

fn main() {
    let mut c_string: arr_type = [0; 20]; 
    let value = CString::new("happy123").unwrap();

    c_string.clone_from_slice(value.as_bytes_with_nul().as_signed());
}

答案 1 :(得分:1)

我建议通过同时迭代数组和字符串来自动更新每个字符,并将字符串字符分配给数组字符。我将最后的\0添加到Rust-string。

#![feature(libc)]
extern crate libc;

fn main() {
    use libc::c_char;

    type ArrType = [c_char; 20]; // arr_type is the type in C
    let mut arr : ArrType = [0; 20]; 

    let s = "happy123\0";
    assert!(s.len() <= arr.len());
    for (a, c) in arr.iter_mut().zip(s.bytes()) {
        *a = c as i8;
    }
}

Try it out in the PlayPen


在大多数情况下,llvm会将该循环优化为memcopy。

define internal void @_ZN4main20hf4c098c7157f3263faaE() unnamed_addr #0 {
entry-block:
  %0 = alloca %"2.core::str::Bytes", align 8
  %arg4 = alloca %str_slice, align 8
  %1 = bitcast %"2.core::str::Bytes"* %0 to i8*
  call void @llvm.lifetime.start(i64 16, i8* %1)
  %2 = bitcast %str_slice* %arg4 to i8*
  call void @llvm.lifetime.start(i64 16, i8* %2)
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* %2, i8* bitcast (%str_slice* @const26 to i8*), i64 16, i32 8, i1 false)
  call void @_ZN3str3str5bytes20h68b1cf722a654e56XOgE(%"2.core::str::Bytes"* noalias nocapture sret dereferenceable(16) %0, %str_slice* noalias nocapture dereferenceable(16) %arg4)
  call void @llvm.lifetime.end(i64 16, i8* %2)
  call void @llvm.lifetime.end(i64 16, i8* %1) #3, !alias.scope !0, !noalias !3
  call void @llvm.lifetime.end(i64 16, i8* %1)
  ret void
}

答案 2 :(得分:1)

  

在C / C ++中,我可以使用memcpy,strcpy等...

在Rust中使用它们也没有问题:

extern { fn memcpy(dst: *mut libc::c_void, src: *const libc::c_void, len: libc::size_t); }

let t_slice: &mut [c_char] = &mut arr;
unsafe {
    memcpy(t_slice.as_mut_ptr() as *mut libc::c_void, 
        s_ptr as *const libc::c_void, 
        c_s.as_bytes_with_nul().len() as libc::size_t);
}

但最好使用ptr module中的防锈等效std::ptr::copy_nonoverlapping

let t_slice: &mut [c_char] = &mut arr;
unsafe {
    ptr::copy_nonoverlapping(t_slice.as_mut_ptr(), s_ptr, c_s.as_bytes_with_nul().len());
}

但是,您应该注意unsafe块,因此您有责任检查arr中是否有足够的空间。