C - 使用特殊字符格式化字符串大小

问题描述 投票:5回答:1

所以,我试图从字符串格式良好的比赛中打印灯具,但我发现只要有'é'或'í'或'á'这样的特殊字符,即使我指定了最大值,它也会打印+1大小长度。

代码在哪里:

printf("=> %-25s (%d) vs (%d) \t%-25s\n", f->home_team_name, f->goals_home_team, f->goals_away_team, f->away_team_name);

对于具有这些字符的团队,输出如下:

=> Palmeiras               (2) vs (0)   Botafogo               
=> Atlético Mineiro       (4) vs (3)    Grémio                
=> Atlético PR            (3) vs (0)    Palmeiras              
=> Botafogo                (2) vs (2)   Cruzeiro   

但我希望输出看起来像,即使使用特殊字符:

=> Tottenham Hotspur FC    (0) vs (0)   Leicester City FC      
=> West Ham United FC      (0) vs (0)   Everton FC             
=> Burnley FC              (0) vs (0)   AFC Bournemouth   

我试图寻找格式化标志但无法找到解决方案。

c string format special-characters
1个回答
3
投票

printf中的格式字符串不考虑多字节字符。

一种可能的解决方案是通过mbstowcs函数计算字符串的宽字符。然后从检查的字符串的长度(即以字节为单位)中减去所获得的计数。这产生(非负)“补偿值”,可以添加到printf的格式字段宽度。

mbstowcs函数描述为:

将多字节字符串从src指向其第一个元素的数组转换为其宽字符表示形式。转换后的字符存储在dst指向的数组的连续元素中。只有len宽字符写入目标数组。

在您的情况下,这意味着UTF-8编码的八位字节(在char数组中表示)被转换为一些宽的表示,这保证任何多字节字符(最多为特定于语言环境的MB_CUR_MAX字节)可以由不超过一个wchar_t编码宾语。

C11标准的相关引用载于7.19 / 2通用定义<stddef.h>

wchar_t

这是一个整数类型,其值范围可以表示支持的语言环境中指定的最大扩展字符集的所有成员的不同代码;

例如,在Linux平台上,宽字符最有可能在UCS-4中表示(称为UTF-32)。

这是一个概念证明:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>

static inline size_t widestrlen(const char *str)
{
    return mbstowcs(NULL, str, strlen(str));
}

static inline size_t compensation(const char *str)
{
    return strlen(str) - widestrlen(str);
}

int main(void)
{
    setlocale(LC_CTYPE, "");

    // Print some debugging information regarding selected locale
    printf("Current locale for LC_TYPE category: %s\n", setlocale(LC_CTYPE, NULL));
    printf("Maximum number of bytes in a multibyte character: %zu\n", MB_CUR_MAX);
    printf("Does current encoding support shift states? : %s\n\n", mblen(NULL, 0) ? "Yes" : "No");

    int goals_home_teams[] = { 4, 0 };
    int goals_away_teams[] = { 3, 0 };
    const char *home_team_names[] = { "Atlético Mineiro", "West Ham United FC" };
    const char *away_team_names[] = { "Grémio", "Everton FC" };

    for (int i = 0; i < 2; i++)
    {
        printf("=> %-*s (%d) vs (%d) \t%-*s\n",
            25 + (int) compensation(home_team_names[i]),
            home_team_names[i], goals_home_teams[i], goals_away_teams[i],
            25 + (int) compensation(away_team_names[i]),
            away_team_names[i]);
    }
    return 0;
}

结果:

Current locale for LC_TYPE category: en_US.UTF-8
Maximum number of bytes in a multibyte character: 6
Does current encoding support shift states? : No

=> Atlético Mineiro          (4) vs (3)     Grémio                   
=> West Ham United FC        (0) vs (0)     Everton FC  
© www.soinside.com 2019 - 2024. All rights reserved.