2

我正在尝试绘制一个具有给定宽度和高度的正方形。我正在尝试在使用 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 的正确用法,您只需要执行一次吗?或者更多?

任何帮助提前感谢!

4

1 回答 1

4

另外如您所见,我不了解 setlocale 的正确用法,您只需要执行一次吗?或者更多?

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

但是当我用一些 int 值运行这段代码时,我得到一个看似随机的空格和换行符的输出。

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

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

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

还要注意,宽度wchar_t取决于实现,它可以小到八位。a 不一定可以表示任意 Unicode 字符——事实上, 16 位宽wchar_t是很常见的,实际上对于 Unicode 的 21 位代码空间中的大多数字符来说,这还不够宽。您可能会以两个单元的形式获得更宽字符的内部表示,例如 UTF-16 代理对,但您也可能不会——其中大部分留给单独的实现。wchar_t

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

那你该怎么办? 首先,我建议使用仅使用基本字符集(需要 C2011)表示的 UTF-8 字符串文字将源字符集排除在等式之外:

#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"

请注意,生成的字符串是正常的,而不是宽的,因此您不应该对它们使用面向宽的输出函数。相反,请使用普通的printf,putchar

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

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

于 2019-07-17T21:26:03.853 回答