04、Flutter FFI 字符串

 
Flutter FFI 学习笔记系列

  1. 《Flutter FFI 最简示例》
  2. 《Flutter FFI 基础数据类型》
  3. 《Flutter FFI 函数》
  4. 《Flutter FFI 字符串》
  5. 《Flutter FFI 结构体》
  6. 《Flutter FFI 类》
  7. 《Flutter FFI 数组》
  8. 《Flutter FFI 内存管理》
  9. 《Flutter FFI Dart Native API》
      
     

在前面的章节中,介绍了基础数据类型和函数的知识,在这一章节中,将介绍 Dart 与 C 语言的字符串传递方式。
  
 

1、C 语言返回字符串给 Dart

1.1 C 语言字符串

  C语言中的字符串是以 “\0” 为结束标记的,假设 C 语言中定义了一个 greetingString() 函数,返回了问候语:

#include <stdint.h>

#define DART_API extern "C" __attribute__((visibility("default"))) __attribute__((used))

DART_API const char *greetString() {
    return "Hello from Native\0";
}

  那么,我们如何在 Dart 中使用该函数返回的字符串呢?
 

1.2 引入 ffi 库

  在 Dart 中,可以引用 ffi 库来帮助我们快速实现字符串转换功能。
  首先,打开 pubspec.yaml 文件,增加依赖库:

dependencies:
  ffi: ^1.0.0

  然后,执行 pub get ,下载依赖。此时便可以在 Dart 代码中引入 ffi 库:

import 'package:ffi/ffi.dart';

 

1.3 C字符串转Dart字符串

  下面接下来看一下如何使用ffi 库把 C 的字符串映射到 Dart 中使用。
  
  首先,在 Dart 中定义两个函数类型,用于映射 C 中的 greetingString() 函数:

typedef Native_greetingString = Pointer<Int8> Function();

typedef FFI_greetingString = Pointer<Int8> Function();

  接着,在 Dat 中编写代码调用 C 函数,并将 C 的字符串转为 Dart 的字符串:

//加载符号
DynamicLibrary nativeApi = Platform.isAndroid
    ? DynamicLibrary.open("libnative_ffi.so")
    : DynamicLibrary.process();
    
//查找目标函数
FFI_greetingString greetingFunc = nativeApi
	.lookupFunction<Native_greetingString, FFI_greetingString>("greetString");
    
//调用 greetString 函数,并将结果转为 Dart String.
Pointer<Int8> result = greetingFunc();
String greeting = result.cast<Utf8>().toDartString();

//打印结果
print("greeting=$greeting");
    
//输出结果
//greeting=Hello from Native

  代码说明:

  • C 语言中的字符串用 char* 表示,字符串是以 “\0” 为结束标记;
  • Dart 需要使用 Pointer<Int8> 表示 C 语言中的 char* 类型;
  • toDartString() 是 ffi 库提供的函数,用于将 Pointer<Utf8> 转为 Dart String。因此需要将 Pointer<Int8> 转为 Pointer<Utf8> 类型。当然,也可以直接用 Pointer<Utf8> 类型表示 C 中的字符串;
  • greetString()返回的是一个字符串常量,因此不需要释放内存。

  toDartString() 的工作原理是把 cahr* 转为 char 数组,然后采用 utf8 进行解码。该函数定义如下:

/// Extension method for converting a`Pointer<Utf8>` to a [String].
extension Utf8Pointer on Pointer<Utf8> {
  /// The number of UTF-8 code units in this zero-terminated UTF-8 string.
  ///
  /// The UTF-8 code units of the strings are the non-zero code units up to the
  /// first zero code unit.
  int get length {
    _ensureNotNullptr('length');
    final codeUnits = cast<Uint8>();
    return _length(codeUnits);
  }

  /// Converts this UTF-8 encoded string to a Dart string.
  ///
  /// Decodes the UTF-8 code units of this zero-terminated byte array as
  /// Unicode code points and creates a Dart string containing those code
  /// points.
  ///
  /// If [length] is provided, zero-termination is ignored and the result can
  /// contain NUL characters.
  ///
  /// If [length] is not provided, the returned string is the string up til
  /// but not including  the first NUL character.
  String toDartString({int? length}) {
    _ensureNotNullptr('toDartString');
    final codeUnits = cast<Uint8>();
    if (length != null) {
      RangeError.checkNotNegative(length, 'length');
    } else {
      length = _length(codeUnits);
    }
    return utf8.decode(codeUnits.asTypedList(length));
  }

  static int _length(Pointer<Uint8> codeUnits) {
    var length = 0;
    while (codeUnits[length] != 0) {
      length++;
    }
    return length;
  }

  void _ensureNotNullptr(String operation) {
    if (this == nullptr) {
      throw UnsupportedError(
          "Operation '$operation' not allowed on a 'nullptr'.");
    }
  }
}

  说明:

  • 除了 Utf8 之外,ffi 库还提供了 Utf16,有兴趣可以了解一下。
     

2、Dart 返回字符串给 C

  上面介绍了如何把 C 的字符串转为 Dart 的字符串,接下来看看反过来是怎样实现的。
  
  首先,在 C 中定义两个函数:

  • reverse_string() 用于反转字符串;
  • free_string() 用于释放字符串所占用的内存;
#include <stdint.h>

#define DART_API extern "C" __attribute__((visibility("default"))) __attribute__((used))
    
DART_API char *reverse_string(const char *str, int length)
{
    // Allocates native memory in C.
    char *reversed_str = (char *)malloc((length + 1) * sizeof(char));
    for (int i = 0; i < length; i++)
    {
        reversed_str[length - i - 1] = str[i];
    }
    reversed_str[length] = '\0';
    return reversed_str;
}

void free_string(char *str)
{
    // Free native memory in C which was allocated in C.
    free(str);
}

  然后,在 Dart 中定义与 C 相对应的函数类型:

typedef Native_reverseString = Pointer<Int8> Function(Pointer<Int8>, Int32);
typedef FFI_reverseString = Pointer<Int8> Function(Pointer<Int8>, int);

typedef Native_freeString = Void Function(Pointer<Int8>);
typedef FFI_freeString = void Function(Pointer<Int8>);

  最后,在 Dart 调用 C 的函数,将 Dart 字符串转成 C 的字符串,并进行内存释放;

//加载库
DynamicLibrary nativeApi = Platform.isAndroid
    ? DynamicLibrary.open("libnative_ffi.so")
    : DynamicLibrary.process();

//查找目标函数 - reverse_string
FFI_reverseString reverseFunc = nativeApi
    .lookupFunction<Native_reverseString, FFI_reverseString>("reverse_string");

//查找目标函数 - free_string
FFI_freeString freeFunc = nativeApi
    .lookupFunction<Native_freeString, FFI_freeString>("free_string");

//调用函数
String value = "ABCDEFG";
Pointer<Int8> nativeValue = value.toNativeUtf8().cast<Int8>();
Pointer<Int8> reverseValue = reverseFunc(nativeValue, value.length);

print("original.value=$value");
print("reverse.value=${reverseValue.cast<Utf8>().toDartString()}");

//释放字符串所占用的内存
freeFunc(nativeValue); //或者调用 calloc.free(nativeValue);
freeFunc(reverseValue); //或者调用 calloc.free(reverseValue);

//输出结果:
//original.value=ABCDEFG
//reverse.value=GFEDCBA

说明:

  • 因为 C 中的 reverse_string() 使用 malloc 分配了内存,因此在使用完之后,必须使用 free 释放内存,否则会有内存泄漏;
  • toNativeUtf8() 是由 ffi 库提供的API,调用该函数时会在 Native 中分配内存,因此使用完后也需要释放内存。
    也可以使用 calloc.free() 来释放由 malloc 分配的内存;

  toNativeUtf8() 函数的作用将 Dart 字符串转为 Pointer<Utf8> ,以便在 C 中使用 Dart 字符串。
  toNativeUtf8() 其工作原理是把 Dart 字符串转为 Utf8,然后在 C 中分配一个 char 数组,接着再复制 Utf8 的内存数据。该函数的定义如下:

/// Extension method for converting a [String] to a `Pointer<Utf8>`.
extension StringUtf8Pointer on String {
  /// Creates a zero-terminated [Utf8] code-unit array from this String.
  ///
  /// If this [String] contains NUL characters, converting it back to a string
  /// using [Utf8Pointer.toDartString] will truncate the result if a length is
  /// not passed.
  ///
  /// Unpaired surrogate code points in this [String] will be encoded as
  /// replacement characters (U+FFFD, encoded as the bytes 0xEF 0xBF 0xBD) in
  /// the UTF-8 encoded result. See [Utf8Encoder] for details on encoding.
  ///
  /// Returns an [allocator]-allocated pointer to the result.
  Pointer<Utf8> toNativeUtf8({Allocator allocator = malloc}) {
    final units = utf8.encode(this);
    final Pointer<Uint8> result = allocator<Uint8>(units.length + 1);
    final Uint8List nativeString = result.asTypedList(units.length + 1);
    nativeString.setAll(0, units);
    nativeString[units.length] = 0;
    return result.cast();
  }
}

  说明:

  • 同样的,ffi 库也提供了 Utf16toNativeUtf16,有兴趣可以了解一下。

 

3、总结

  
  上面介绍 C 字符串与 Dart 字符串的相互转换,还提到了内存的分配和释放。在后面的章节中,将会介绍结构体、数组、内存管理等知识,欢迎关注。
  
  
 

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Flutter中,可以使用Dart语言提供的字符串截取方法来截取字符串。有几种常用的方法可以实现字符串截取操作。 一种方法是使用Dart的substring()方法。这个方法可以通过指定起始位置和结束位置来截取字符串的一部分。例如,使用str.substring(startIndex, endIndex)可以截取字符串str从startIndex位置到endIndex位置的子字符串。 另一种方法是使用split()方法。通过传入一个分隔符作为参数,split()方法可以将字符串拆分成一个字符串数组。例如,可以使用str.split(",")来以逗号作为分隔符来拆分字符串str,并返回一个包含拆分后子字符串的数组。 还可以使用正则表达式来进行字符串截取。可以使用split()方法,其中传入的参数是一个正则表达式,这样可以根据正则表达式来拆分字符串。不过需要注意,使用这种方式截取字符串会有一定的性能损耗,因为分析正则表达式非常耗时。 综上所述,在Flutter中可以通过substring()方法、split()方法和正则表达式来实现字符串截取操作。具体使用哪种方法取决于你的具体需求和性能要求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [字符串截取的常用方法](https://blog.csdn.net/weixin_31319319/article/details/112044745)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值