在c中使用宽字符时的奇怪空间

时间:2019-07-17 19:56:10

标签: c

我正在尝试绘制一个具有给定宽度和高度的正方形。 我正在尝试使用Unicode中的框字符。 我正在使用此代码:

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

#include "string_prints.h"

#define VERTICAL_PIPE L"║"
#define HORIZONTAL_PIPE L"═"
#define UP_RIGHT_CORNER L"╗"
#define UP_LEFT_CORNER L"╔"
#define DOWN_RIGHT_CORNER L"╝"
#define DOWN_LEFT_CORNER L"╚"

// Function to print the top line
void DrawUpLine(int w){
    setlocale(LC_ALL, "");
    wprintf(UP_LEFT_CORNER);
    for (int i = 0; i < w; i++)
    {
        wprintf(HORIZONTAL_PIPE);
    }
    wprintf(UP_RIGHT_CORNER);
}

// Function to print the sides
void DrawSides(int w, int h){
    setlocale(LC_ALL, "");
    for (int i = 0; i < h; i++)
    {
        wprintf(VERTICAL_PIPE);
        for (int j = 0; j < w; j++)
        {
            putchar(' ');
        }
        wprintf(VERTICAL_PIPE);
        putchar('\n');
    }
}

// Function to print the bottom line
void DrawDownLine(int w){
    setlocale(LC_ALL, "");
    wprintf(DOWN_LEFT_CORNER);
    for (int i = 0; i < w; i++)
    {
        wprintf(HORIZONTAL_PIPE);
    }
    wprintf(DOWN_RIGHT_CORNER);
}

void DrawFrame(int w, int h){
    DrawUpLine(w);
    putchar('\n');
    DrawSides(w, h);
    putchar('\n');
    DrawDownLine(w);
}

但是当我使用一些int值运行此代码时,我得到的输出似乎是随机的空格和换行符(尽管管道看起来是正确的顺序)。

正从main.c的标题中调用它,如下所示:

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

#include "string_prints.h"

int main(){
    DrawFrame(10, 20); // Calling the function
    return 0;
}

您还可以看到我不了解setlocale的正确用法,您是否只需要执行一次?或更多?

任何帮助先感谢您!

1 个答案:

答案 0 :(得分:3)

  

您还可以看到我不了解setlocale的正确用法,您是否只需要执行一次?或更多?

通过setlocale()应用的语言环境更改在调用过程中保持不变。除非您要进行多次更改,否则无需多次调用该函数。但是,您要做需要为其命名的语言环境可满足您的预期目的,或者如果您使用空字符串调用它,则您或程序用户确实需要确保定义各种环境变量的环境变量区域设置类别设置为适合该目的的值。

  

但是当我使用一些int值运行此代码时,我得到了输出   似乎是随机的空格和换行符。

这听起来像是字符编码不匹配的结果,甚至是两个(但也请参见下文):

  • 可能存在运行时不匹配的情况,因为您告诉程序用于输出的语言环境与显示程序输出的输出设备(例如例如终端)期望的语言环境不匹配,和
  • 源文件的实际字符编码与编译器将其解释为具有的编码之间也可能存在编译时不匹配的情况。

此外,尽管使用了宽字符串文字语法,但取决于C的实现,除C基本集以外的其他字符也可能出现在您的源代码中。宽泛的语法主要指定文字(类型wchar_t的元素)的存储形式,而不是什么字符值有效或如何解释。

还要注意,wchar_t的宽度取决于实现,并且可以小到8位。 wchar_t不一定可以表示任意Unicode字符-实际上,wchar_t的宽度为16位是很常见的,实际上不是宽度足以容纳Unicode 21位代码空间中的大多数字符。您可能会以两单元形式获得内部较宽字符的内部表示形式,例如UTF-16代理对,但您可能也不会这样做-很大一部分留给各个实现。

在这些事情中,编译器期望什么编码,在什么情况下以及如何影响这些都是依赖于实现的。例如,对于GCC,默认的源(“输入”)字符集为UTF-8,您可以通过其-finput-charset选项定义其他字符集。如果愿意,还可以通过-fexec-charset-fwide-exec-charset选项指定标准和宽执行字符集。 GCC在编译时(从源字符集到执行字符集)和运行时(从执行字符集到语言环境字符集)都依赖iconv进行转换。其他实现具有其他选项(或没有),它们具有自己的语义。

那你应该怎么做?首先,我建议使用仅使用基本字符集表示的UTF-8字符串文字将源字符集从等式中取出(要求C2011):

#define VERTICAL_PIPE     u8"\xe2\x95\x91"
#define HORIZONTAL_PIPE   u8"\xe2\x95\x90"
#define UP_RIGHT_CORNER   u8"\xe2\x95\x97"
#define UP_LEFT_CORNER    u8"\xe2\x95\x94"
#define DOWN_RIGHT_CORNER u8"\xe2\x95\x9d"
#define DOWN_LEFT_CORNER  u8"\xe2\x95\x9a"

请注意,结果字符串是正常的,而不是宽字符串,因此您不应将它们与宽字符串一起使用。而是使用普通的printfputchar

这给我们的代码带来了另一个问题:在未采取明确措施进行切换的情况下,切勿混用向同一流写入的面向宽字节和面向字节的函数freopen;请参阅标准的paragraph 7.21.2/4。在实践中,将两者混合在一起可能会产生错误的结果。

然后还要确保为您的实际环境正确设置了本地环境变量。可能已经很不错了,但是值得一试。