将整数转换为字符串的C++兼容函数,适用于Matlab Coder。

6

我正在使用 Matlab Coder 将一些 Matlab 代码转换为 C++,但是我在将整数转换为字符串时遇到了问题。

int2str() 不支持代码生成,因此我必须找到其他方法将整数转换为字符串。我已经尝试过谷歌搜索,但没有成功。这真的可能吗?


1
num2str被支持吗?这是这种类型中最通用的函数。 - buzjwa
num2str也不受支持。 - ein123
4个回答

8
这可以手动完成(虽然非常痛苦)。
function s = thePainfulInt2Str( n )
s = '';
is_pos = n > 0; % //save sign
n = abs(n); %// work with positive
while n > 0
    c = mod( n, 10 ); % get current character
    s = [uint8(c+'0'),s]; %// add the character
    n = ( n -  c ) / 10; %// "chop" it off and continue
end
if ~is_pos
    s = ['-',s]; %// add the sign
end

1
我认为你需要将 while 条件改为 n>0 - Luis Mendo
1
小问题,对于有符号整数类型的intmin,这会打印出一个不正确的字符串,因为abs(n)会饱和。例如,abs(intmin('int32'))将饱和,以便打印-intmax('int32')。可以添加一个检查来检查intmin,然后将s(end)加一。 - Ryan Livingston

7

sprintf是另一个非常基本的函数,所以它可能也可以在C++中使用:

x = int64(1948)
str = sprintf('%i',x)

这也是int2str使用的基础功能。


根据支持的函数综合列表(由评论中的Matt指出),令人惊讶的是,sprintf不受支持。然而,有一个未经记录的辅助函数(因此不在列表中)sprintfc,它似乎可以工作,并且可以等效使用:

str = sprintfc('%i',x)

我也是这么想的。在写答案之前,我检查了这里的列表[http://ch.mathworks.com/help/coder/ug/functions-supported-for-code-generation--alphabetical-list.html],`sprintf`似乎不被支持。 - Matt
1
sprintf也不被支持。 - ein123
1
@ein123,不知道sprintfc这个未记录的函数怎么样?你能检查一下吗? - Robert Seifert
sprintfc 确实似乎可行。我想那就是最顺畅的解决方案了。谢谢!@thewaywewalk - ein123
其实我现在发现sprintfc不起作用了。一开始它没有警告,原因不明,但现在它有了。所以我想你必须手动处理。@thewaywewalk - ein123
@ein123 很好知道!你应该考虑接受 Shai 的答案,因为那个回答对你有帮助。 - Robert Seifert

2
我将使用以下方法来启用Matlab Coder的sprintf通用功能的解决方法:
1) 创建名为“sprintf.m”的以下m文件,最好放在不在您的Matlab路径上的文件夹中:
function s = sprintf(f, varargin)

if (coder.target('MATLAB'))
    s = builtin('sprintf',f,varargin{:});
elseif (coder.target('MEX'))
    s = builtin('sprintf',f,varargin{:});
else
    coder.cinclude('stdio.h');
    s = char(zeros(1024,1));
    cf = [f,0]; % NULL-terminated string for use in C
    coder.ceval('sprintf_s', coder.ref(s(1)), int32(1024), coder.rref(cf(1)), varargin{:});
end

2)确保不通过coder.extrinsic指定sprintf为外部函数。

3)在生成代码时,如果您使用codegen函数,则需要将新创建的“sprintf.m”所在的文件夹指定为附加包含目录。这可以通过-I开关完成。如果您使用Coder App,则可以在“更多设置 -> 自定义代码 -> 附加包含目录”下的“生成”选项卡中完成。

4)将整数转换为字符串,方法如下:s=sprintf('%d',int32(n));

注:

  • 指定的“sprintf.m”会覆盖内置的sprintf函数,并且每次从生成的代码中调用sprintf时都会执行该文件。通过将此文件放置在Matlab路径之外的文件夹中,可以避免从其他代码中调用它。调用coder.target还有助于在正常的Matlab会话或MEX文件中导航回内置函数。
  • 上述代码将结果限制为1023个字符(结尾处需要一个终止零)。调用sprintf_s指示C++编译器在结果超过此值时抛出运行时异常。这可以防止仅在较晚时才发现的内存损坏,并且更难追溯到有问题的调用。此限制可以根据您自己的要求进行修改。
  • 在将数值类型传递给sprintf之前,必须将其强制转换为正确的类别,例如将其转换为int32以匹配格式字符串中的%d。在使用Matlab Coder时,使用fprintf也需要遵守此要求。但是,在fprintf情况下,Matlab Coder会为您捕获类型错误。对于sprintf,C++编译器可能会失败,或者生成的字符串可能包含错误。
  • 手动将字符串参数NULL结尾才能在C调用中使用,因为Matlab Coder不会自动执行此操作(感谢Ryan Livingston指出)。上述代码确保格式字符串f已NULL结尾,但其他字符串参数的NULL结尾仍然是调用函数的责任。
  • 此代码在Windows平台上使用Visual Studio C++编译器和Matlab R2016a(Matlab Coder 3.1)进行了测试,但预计在大多数其他环境中也可以正常工作。

1
不要忘记对将作为C字符串的任何参数进行空终止:cf = [f, 0]; coder.ceval(...,coder.rref(cf(1)));。由MATLAB Coder生成的字符数组没有空终止符。如果不这样做,将导致生成的代码出现未定义的行为。 - Ryan Livingston

2

编辑:从MATLAB R2018a开始,sprintf支持由MATLAB Coder生成的代码。

截至R2018a的答案

您还可以使用coder.ceval调用C运行时的sprintfsnprintf。这样做的好处是,它使得支持浮点输入变得非常容易。您也可以通过调整格式字符串来更改所需的格式。

假设您的编译器提供snprintf,则可以使用以下命令:

function s = cint2str(x)
%#codegen
if coder.target('MATLAB')
    s = int2str(x);
else
    coder.cinclude('<stdio.h>');
    assert(isfloat(x) || isinteger(x), 'x must be a float or an integer');
    assert(x == floor(x) && isfinite(x), 'x must be a finite integer value');
    if isinteger(x)
        switch class(x)
            % Set up for Win64, change to match your target
            case {'int8','int16','int32'}
                fmt = '%d';
            case 'int64'
                fmt = '%lld';
            case {'uint8','uint16','uint32'}
                fmt = '%u';
            otherwise
                fmt = '%llu';
        end
    else
        fmt = '%.0f';
    end
    % NULL-terminate for C
    cfmt = [fmt, 0];

    % Set up external C types
    nt = coder.opaque('int','0');
    szt = coder.opaque('size_t','0');
    NULL = coder.opaque('char*','NULL');

    % Query length
    nt = coder.ceval('snprintf',NULL,szt,coder.rref(cfmt),x);
    n = cast(nt,'int32');
    ns = n+1;  % +1 for trailing null

    % Allocate and format
    s = coder.nullcopy(blanks(ns));
    nt = coder.ceval('snprintf',coder.ref(s),cast(ns,'like',szt),coder.rref(cfmt),x);
    assert(cast(nt,'int32') == n, 'Failed to format string');
end

请注意,您可能需要调整格式字符串以匹配运行硬件,因为这假设 long long 可用并将64位整数映射到它上面。

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