[英]Read unicode characters from file in C
我正在嘗試從.csv文件讀取UTF-8字符串,然后將其寫入控制台。
a.csv內容:
Gijón
經過一整天的研究,我發現執行這種操作的正確方法應該類似於以下內容:
int main(int argc, char *argv[])
{
char *locale = setlocale(LC_ALL, "");
printf("locale: %s\n", locale);
const int MAX_LINE_SIZE = 1024;
char line[MAX_LINE_SIZE];
wchar_t wline[MAX_LINE_SIZE];
// Attempt 0: no special handling
FILE* stream = fopen("a.csv", "r");
fgets(line, MAX_LINE_SIZE, stream);
printf("%s\n", line); // Expected to print "Gijón", prints "Gijón"
fclose(stream);
// Attempt 1: mbstowcs
mbstowcs(wline, line, MAX_LINE_SIZE);
wprintf(L"%ls\n", wline); // Expected to print "Gijón", prints "Gijón"
// Attempt 2: fgetws
stream = fopen("a.csv", "r");
fgetws(wline, MAX_LINE_SIZE, stream);
wprintf(L"%ls\n", wline); // Expected to print "Gijón", prints "Gijón"
fclose(stream);
// Attempt 3: _wfopen
stream = _wfopen(L"a.csv", L"rb");
fgetws(wline, MAX_LINE_SIZE, stream);
wprintf(L"%ls\n", wline); // Expected to print "Gijón", prints ""
fclose(stream);
// Printing command line parameter
mbstowcs(wline, argv[1], MAX_LINE_SIZE);
wprintf(L"%ls\n", wline); // Properly prints "Gijón"
}
但是運行此程序會導致:
.\myprogram.exe Gijón
locale: Spanish_Spain.1252
Gijón
Gijón
Gijón
我認為這不是控制台本身的問題,因為argv[1]
轉換效果很好。
我想念什么?
wchar_t
和Wide char函數( wfopen
等)主要在Windows中用於處理UTF16編碼的Unicode。
UTF8使用char
和相同的ASCII兼容C函數( fopen
等)。要讀取UTF8,可以對ASCII使用相同的C函數。
Windows不完全支持讀取和顯示UTF8,因此必須在UTF8和UTF16之間進行轉換才能正確顯示文本。 Windows 10確實支持控制台Windows的UTF8,請參閱相關主題。
#include <stdio.h>
#include <windows.h>
int main(void)
{
const char* filename = "a.csv";
FILE* fp = fopen(filename, "r");
char buf[1000];
fgets(buf, sizeof(buf), fp);
if(strlen(buf) > 2)
if(strncmp(buf, "\xFF\xFE", 2) == 0)
{
printf("UTF16-LE\n");
fclose(fp);
fp = fopen(filename, "rb");
wchar_t wbuf[1000] = { 0 };
fgets((char*)wbuf, sizeof(buf), fp);
MessageBoxW(0, wbuf, L"UTF16-LE", 0);
return 0;
}
if(strlen(buf) > 3)
if(strncmp(buf, "\xEF\xBB\xBF", 3) == 0)
printf("UTF8 with BOM\n");
//assume UTF8 and convert to UTF16:
int size = MultiByteToWideChar(CP_UTF8, 0, buf, -1, NULL, 0);
wchar_t *utf16 = malloc((size + 1) * sizeof(wchar_t));
MultiByteToWideChar(CP_UTF8, 0, buf, -1, utf16, size);
MessageBoxA(0, buf, "ANSI", 0);
MessageBoxW(0, utf16, L"UTF8 converted", 0);
return 0;
}
如果源文件是UTF8,則基本上將其視為ASCII。 請注意strtok
函數,它們不能處理ASCII范圍之外的輸入字符。 唯一的其他麻煩是當您嘗試在Windows中打印時。 將下面的示例與自定義printf
函數一起使用:
void printf_utf8(const char* format, ...)
{
va_list args;
va_start(args, format);
int len = _vscprintf(format, args) + 1;
char *buf = malloc(len);
vsprintf(buf, format, args);
//convert to UTF16 and print
int wbuf_size = MultiByteToWideChar(CP_UTF8, 0, buf, -1, NULL, 0);
wchar_t *wbuf = malloc((wbuf_size + 1) * sizeof(wchar_t));
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, wbuf_size);
DWORD temp;
HANDLE h = GetStdHandle(STD_OUTPUT_HANDLE);
WriteConsoleW(h, wbuf, wcslen(wbuf), &temp, 0);
free(wbuf);
free(buf);
}
int main(void)
{
FILE* fp = fopen("a.csv", "r");
if(!fp)
return 0;
char buf[1000];
fgets(buf, sizeof(buf), fp);
printf_utf8("Test %s %d\n", buf, 123);
return 0;
}
我認為您必須將寬字符轉換為1252編碼。 1252編碼是8位/字符編碼,將僅支持unicode字符的一小部分。 也許有轉換功能/庫可用。 但是似乎自己寫起來並不太復雜(大switch / case子句)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.