c - 关于使用 unicode 输入和文件输出改进代码的问题

标签 c unicode

我在使用 unicode 字符时遇到了一些问题,我想知道下面的代码是否以熟练的方式完成。简而言之,我想输入2个单词,第一个是blue,第二个是blå。它们将保存在两个不同的文本文件中,然后程序将从文件中读取并在终端中正确打印它们。我主要对有关 unicode、_setmode、宽字符等的行的改进感兴趣。以下是代码:

#include <stdio.h>
#include <locale.h>
#include <wchar.h>
#include <string.h>
#include <io.h>
#include <fcntl.h>

#define _O_U16TEXT 0x20000
#define _O_DEFAULT 0x4000
#define SIZE 1000

typedef struct {
   wchar_t sweWord[SIZE];     //sweWord=Swedish Word
   char engWord[SIZE];        //engWord=English Word
} Word;


void set_mode_to_UTF16() {

   fflush(stdin);
   fflush(stdout);

   _setmode(_fileno(stdin), _O_U16TEXT);
   _setmode(_fileno(stdout), _O_U16TEXT);
}

void set_mode_to_default() {
   _setmode(_fileno(stdin), _O_DEFAULT);
   _setmode(_fileno(stdout), _O_DEFAULT);
}

Word enterWord() {
   Word aWord;

   printf("Enter english word \"blue\": ");
   scanf("%s", aWord.engWord);
   printf("You entered: %s\n", aWord.engWord);

   set_mode_to_UTF16();

   wprintf(L"Enter swedish word \"blå\": ");
   wscanf(L"%ls", aWord.sweWord);
   wprintf(L"You entered: %ls\n", aWord.sweWord);

   set_mode_to_default();

   return aWord;
}

void saveWord(Word aWord) {
   FILE *pFile1;
   FILE *pFile2;

   if(pFile1=fopen("ENGWORD.txt", "w")) {
      fprintf(pFile1, "%s\n", aWord.engWord);
   } else {
      printf("Failed to save ENGWORD!\n");
   }
   fclose(pFile1);
 
   set_mode_to_UTF16();

   if(pFile2=fopen("SWEWORD.txt", "w")) {
      _setmode(_fileno(pFile2), _O_U16TEXT);
      fwprintf(pFile2, L"%ls\n", aWord.sweWord);
   } else {
      wprintf(L"Failed to save SWEWORD!\n");
   }
   fclose(pFile2);

   set_mode_to_default();
} 

Word loadWord() {
   Word aWord;
   FILE *pFile1;
   FILE *pFile2;

   if(pFile1=fopen("ENGWORD.txt", "r")) {
      fscanf(pFile1, "%s", aWord.engWord);
   } else {
      printf("Failed to load ENGWORD!\n");
   }
   fclose(pFile1);

   set_mode_to_UTF16();

   if(pFile2=fopen("SWEWORD.txt", "r")) {
      _setmode(_fileno(pFile2), _O_U16TEXT);
      fwscanf(pFile2, L"%ls\n", aWord.sweWord);
   } else {
      wprintf(L"Failed to save SWEWORD!\n");
   }
   fclose(pFile2);

   set_mode_to_default();

   return aWord;
}

int main(void) {
   int defaultMode;

   defaultMode=_setmode(_fileno(stdin), _O_BINARY);
   printf("Default mode is %d\n", defaultMode);
   _setmode(_fileno(stdin), defaultMode);      //mode is now in default.

   Word wordToSave;
   Word wordToLoad;

   wordToSave=enterWord();
   saveWord(wordToSave);
   wordToLoad=loadWord();

   printf("Loaded english word is %s\n", wordToLoad.engWord);
   set_mode_to_UTF16();
   wprintf(L"Loaded swedish word is %ls\n", wordToLoad.sweWord);
   set_mode_to_default();
   printf("Done! Signing off...\n");

   return 0;
}

我的输出是:

Default mode is 16384

Enter english word "blue": blue
You entered: blue
Enter swedish word "blå": blå
You entered: blå
Loaded english word is blue
Loaded swedish word is blå
Done! Signing off...

有两部分我不确定。首先引述一下该网站的一段话:

https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setmode?view=msvc-170

If you write data to a file stream, explicitly flush the code by using fflush before you use _setmode to change the mode.

我在 set_mode_to_UTF16 函数中执行了此操作,但没有在 set_mode_to_default 函数中执行此操作。为什么它们之间应该有区别?

其次,我看到很多帖子都使用 setlocale 将语言环境更改为 UTF-16。然而,在我的代码中我没有使用它,这让我怀疑我是否做错了什么。

我想知道我是否可以获得前面提到的有关我的代码的一些输入和反馈,如果可能的话,帮助我更好地理解我想知道的两个问题。提前致谢!

我使用 Windows 11、VSCode 和 MINGW-32。

最佳答案

fflush问题

关于fflush,我引用man页面

For output streams, fflush() forces a write of all user-space buffered data for the given output or update stream via the stream's underlying write function.

For input streams associated with seekable files (e.g., disk files, but not pipes or terminals), fflush() discards any buffered data that has been fetched from the underlying file, but has not been consumed by the application.

fflush(标准输入)

此行为记录在 other questions on stack overflow 上。另请参阅下面 William Pursell 的评论。

fflush(标准输出)

这个调用不会造成伤害,我当然会刷新两个方向切换之间的标准输出,但重要的是要注意,当您调用 fclose 时,您会隐式刷新文件缓冲区,我认为更符合您共享的文档所描述的精神,它说“如果将数据写入文件流,请在使用 _setmode 更改模式之前使用 fflush 显式刷新代码。”

仅供引用,fclose man页面内容如下:

The fclose() function flushes the stream pointed to by stream (writing any buffered output data using fflush()) and closes the underlying file descriptor.

我怀疑,在您的 fclose 函数调用中,大部分情况下实际上会发生因切换语言环境而可能损坏的缓冲数据的重要刷新,因为您将单词保存在两个不同的文件中。看看是否可以通过调用 fflush 并将两组数据写入同一个文件来将它们保存在单个文件中,这将是很有趣的!

区域设置问题

对于您的第二个问题,设置区域设置比您在此处手动执行的操作要执行更多的步骤并且通过库函数提供附加功能。

GNU C Library Reference Manual 的第 7 章是一个很好的地方,可以阅读有关语言环境的更多信息以及为什么您可以使用它们来完成您在这里所做的事情。第 7.1 节提供了以下信息,说明更改区域设置除了 UTF 编码宽度之外还会产生哪些影响:

Each locale specifies conventions for several purposes, including the following:

  • What multibyte character sequences are valid, and how they are interpreted (*note Character Set Handling::).
  • Classification of which characters in the local character set are considered alphabetic, and upper- and lower-case conversion conventions (*note Character Handling::).
  • The collating sequence for the local language and character set (*note Collation Functions::).
  • Formatting of numbers and currency amounts (*note General Numeric::).
  • Formatting of dates and times (*note Formatting Calendar Time::).
  • What language to use for output, including error messages (*note Message Translation::).
  • What language to use for user answers to yes-or-no questions (*note Yes-or-No Questions::).
  • What language to use for more complex user input. (The C library doesn’t yet help you implement this.)

Some aspects of adapting to the specified locale are handled automatically by the library subroutines. For example, all your program needs to do in order to use the collating sequence of the chosen locale is to use ‘strcoll’ or ‘strxfrm’ to compare strings.

编辑

我想用我刚刚在另一个上下文中尝试过的东西来更新答案,这可以更清楚地说明为什么您可能会考虑使用区域设置系统。

考虑在以下代码中,更改区域设置如何允许在该区域设置内简单映射字符(例如,L'é'L'É')。虽然我个人没有见过它们,但 libc 引用手册指出,语言环境可以自由定义除 toupper 和 tolower 之外的转换;特别保证这两者存在于任何语言环境中。

    setlocale(LC_ALL, "C.UTF-8");
    wchar_t * wide_chars = L"Here ÃrE sõmé chAracters";
    printf("wchar mapping demo string: %ls\n", wide_chars);

    printf("Converting to upper: ");
    for(size_t i = 0; i < wcslen(wide_chars); i++)
    {
        /* demo of how to do it for any class conversion */
        printf("%lc", towctrans(wide_chars[i], wctrans("toupper")));
    }
    printf("\n");

    printf("Converting to lower: ");
    for(size_t i = 0; i < wcslen(wide_chars); i++)
    {
        /* demo of how to do it using built ins */
        printf("%lc", towlower(wide_chars[i]));
    }
    printf("\n");

输出

wchar mapping demo string: Here ÃrE sõmé chAracters
Converting to upper: HERE ÃRE SÕMÉ CHARACTERS
Converting to lower: here ãre sõmé characters

关于c - 关于使用 unicode 输入和文件输出改进代码的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76607883/

相关文章:

C 自由和结构

c - Pthreads矩阵乘法错误

unicode - Perl 6 标识符中允许什么?

Python:有什么方法可以在多语言(例如中文和英文)字符串上执行此 "hybrid"split()?

python - JSON 加载返回 unicode 而不是字典

unicode - 所有 Unicode 左括号/右括号的列表

c - 在 C 中分配字符 'æ' 、 'ø' 或 'å'

c - Gcc 编译器优化函数内联

objective-c - iOS - 如何为网络套接字创建读写流?

c - 如何在运行基于 graphics.h 的 C 程序时在终端中打印一些信息?