我需要得到适当的波兰语字符“ąćśół "。我使用了一些解决方案,如setlocale
,system chcp
,wchar_t
。只要我不使用文件/列表,一切都很顺利。wscanf
,wprintf
和wchar_t
工作得很好。
但是,如果我试图从文件中读取一些内容并将其保存到列表中(即使是数组中),然后试图将其放到屏幕上,我无法获得正确的波兰语字符,并且在列表的情况下,我会不时地获得不同的结果,例如,**z**,**A2**,就像无处不在的随机字符。我一直试图通过使用
fscanf和
fgets`以及w(宽)变体来获得好的结果,但它不起作用。我做错什么了吗?
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include <locale.h>
struct dyk{
wchar_t line[200];
struct dyk *next;
};
typedef struct dyk dyk;
void printdyk(char name[100]){
dyk *wyp;
wyp = malloc(sizeof(dyk));
wchar_t yt[100];
FILE *dyktando;
dyktando = fopen(name, "r+");
if(dyktando == NULL){
wprintf(L"Błąd otwarcia pliku!\n"); //Can't open file
}else{
fgets(&wyp->line, sizeof(dyk), dyktando); //reading from file and send to the list
wprintf(L"%s\n", wyp->line); //write text from the list on the screen
wchar_t yt[100];
wscanf(L"%s", &yt); //testing strings comparing, so I have to put some variables
int n=strcmp(yt, wyp->line); //str compare
printf("%d", n); //result, it gives me -1 every time
}
fclose(dyktando);
}
我测试的功能与txt文件,内容只有一个字符“”。无法正确读取文件。在main函数的开头,我写了这两行:
system("chcp 852");
setlocale(LC_ALL, ".852");
我使用的是codeblock,mingw 32-gcc编译器,没有标志。
3条答案
按热度按时间2eafrhcq1#
您没有在代码中的任何地方使用
wchar_t
兼容函数。特别是:兼容
wchar_t
的版本是fgetws
。另外,wyp->line
(不带&
运算符)是正确的参数。应该使用
wcscmp
。还要注意的是,当一个函数需要的长度是 characters 而不是 bytes 时,
wchar_t
数组上的sizeof
是不正确的(就像fgetws
一样)。eblbsuwk2#
OP(Amatheon)的一个评论指出,真正的潜在问题是如何使用宽字符函数正确读取文件。
为了确保最大的兼容性和可移植性,让我们限制为C99。考虑以下示例程序:
因为
EINVAL
、EIO
和ENOMEM
errno常量在C标准中没有定义,所以如果您定义了USE_ERRNO_CONSTANTS
预处理器值,get_wide_line()
和get_wide_delimited()
只会设置errno
。get_wide_line()
和get_wide_delimited()
是来自ISO/IEC TR 24731 - 2:2010的getwline()
和getwdelim()
函数的重新实现; POSIX. 1getline()
和getdelim()
函数的宽字符等效项。与fgets()
或fgetws()
不同,它们使用动态分配的缓冲区来保存行,因此除了可用内存之外,没有固定的行长度限制。我已经明确地将代码标记为使用Creative Commons Zero许可证:版权所有© 2018这意味着你可以在你自己的代码中使用它,无论你想要什么许可证。
注:我真的很希望用户推动他们的供应商和C标准委员会成员在下一版本的C标准中将这些包含在标准C库部分中。从上面可以看到,它们已经可以在标准C中实现了;只是C库本身可以更有效地做同样的事情。GNU C库就是一个完美的例子(尽管由于缺乏标准化,他们也在拖延实现)。试想一下,如果人们使用
getline()
/getdelim()
/getwline()
/getwdelim()
而不是fgets()
/fgetws()
,会避免多少缓冲区溢出错误!也避免了必须考虑在每种情况下最大合理的线长度是多少。双赢!(In事实上,我们可以将返回类型切换为
size_t
,并使用0
代替-1
作为错误指示符。这将把对C标准案文的修改限制在增加四项功能。它伤心和激怒我没有尽头,有这样一个重要的一组琐碎的功能,如此无情和无知的忽视,没有合理的理由。请尽你所能,不断地、无情地向你的供应商和任何你能接触到的C标准委员会成员抱怨这一点。你和他们都是值得的)。该计划的基本部分是
if (!setlocale(LC_ALL, ""))
这告诉C库使用用户指定的区域设置。
请不要将区域设置值硬编码到您的程序中。在大多数操作系统中,您所需要做的就是在运行程序之前将
LANG
或LC_ALL
环境变量更改为您想要使用的区域设置。您可能会认为 "好吧,这次我可以硬编码它,因为这是用于此数据的区域设置",但即使这样也可能是错误的,因为可以随时创建新的区域设置。当字符集部分被硬编码时,这尤其令人讨厌。例如,在西欧使用的ISO 8859单字节字符集是ISO 8859 - 15,而不是ISO 8859 - 1,因为ISO 8859 - 15中有€字符,而ISO 8859 - 1中没有。如果你在程序中硬编码了ISO 8859 - 1,那么它根本无法正确处理€字符。
if (fwide(stream, 1) < 1)
用于stdout
和文件句柄虽然C库在内部确实根据您第一次在文件句柄上使用的I/O函数的类型来执行与
fwide()
调用等效的操作,但显式检查要好得多。特别是,如果C库不能支持对句柄表示的文件或流的宽I/O,
fwide()
将返回负数。(除非第二个参数也是零,否则它永远不应该返回零;由于标准化中的问题,我建议在这种情况下使用严格的返回值检查方法,以捕获那些决定尽可能让程序员在编写可移植代码的同时在技术上仍然满足标准文本的供应商,就像微软正在做的那样。他们甚至在C标准委员会中安插了他们自己的代表,这样他们就可以调整C11,使其远离他们不想支持的C99特性,并获得他们自己的非标准扩展的批准,这些扩展以前没有人使用过,以帮助开发人员编写可移植的C代码。是的,我一点也不相信他们的行为。)ssize_t len = get_wide_line(&line, &size, handle);
如果在第一次调用
get_wide_line()
或get_wide_delimited()
之前初始化wchar_t *line = NULL;
和size_t size = 0;
,则该函数将根据需要动态调整缓冲区的大小。当且仅当发生错误时,返回值为负。(函数不应返回零。)
当成功读取一行时,返回值反映缓冲区中宽字符的数量,包括分隔符(对于
get_wide_delimited()
,为换行符L'\n'
),并且总是正值(大于零)。缓冲区中的内容将具有终止宽字符串结束字符L'\0'
,但它不计入返回值。请注意,当分隔符不是
L'\0'
时,缓冲区可能包含嵌入的宽nul字符L'\0'
。在这种情况下,len > wcslen(line)
。上面的示例程序跳过每个输入行的任何前导空格,并在第一个换行符(
L'\n'
)、回车符(L'\r'
)或nul(L'\0'
)处修剪该行。因此,只检查返回值len
是否成功(大于零的正返回值)。free(line); line = NULL; size = 0;
在不再需要该行内容的任何时候,可以丢弃该行。我建议显式地将行指针设置为NULL,并将大小设置为零,以避免释放后使用的错误。此外,这允许任何后续的
get_wide_line()
或get_wide_delimited()
正确地动态分配新的缓冲区。ferror(handle)
宽输入功能失败后就像窄流和EOF一样,有两种情况下宽输入函数可能会返回
WEOF
(或返回-1,具体取决于函数):因为没有更多的输入,或者因为发生了读取错误。没有任何理由编写忽略读或写错误而不向用户报告的计算机程序。当然,它们是罕见的,但还没有罕见到程序员可以理智地认为它们永远不会发生。(事实上,闪存存储器的电路很脆弱,存储在脆弱的塑料 shell 中,承受着人类大小的压力(我一次又一次地坐在我的电脑上),错误并不罕见。)这是邪恶的,就像做饭的人懒得洗手一样,时不时地导致粪便细菌爆发。不要成为一个粪便细菌传播者等效的程序员。
假设你有一个粗心的讲师,他不允许你使用上面的
get_wide_line()
或get_wide_delimited()
函数。别担心。我们可以使用
fgetws()
实现相同的程序,如果我们将line限制在某个固定的上限(宽字符)。比这更长的行将改为两行或更多行:除了用于读取每一行的函数之外,不同之处在于,我没有将while循环条件保持为
while ((p = fgetws(line, ...))) { ... }
,而是改为我认为更具可读性的while (1) { p = fgetws(line, ...); if (!p) break; ...
形式。我故意先展示较长、较复杂的代码,最后展示较简单的代码,希望您能看到较复杂的代码实际上是较简单的
main()
--如果我们不只是计算代码行数或类似的愚蠢行为,而是看看有多少出错的机会。正如OP自己在评论中所写的那样,传递给
fgets()
或fgetws()
的缓冲区的大小是一个真实的的问题。有一些经验法则,但它们都遭受了对编辑的脆弱性(特别是数组和指针之间的差异)。对于getline()
/getdelim()
/getwline()
/getwdelim()
/get_wide_line()
/get_wide_delimited()
,经验法则是wchar_t *line = NULL; size_t size = 0; ssize_t len;
和len = get_wide_line(&line, &size, handle);
。没有变化,简单的记忆和使用。此外,它还摆脱了任何固定的限制。nuypyhwy3#
我有一个有趣的解决方案,为我工作后,小时的研究。我还遇到了一个问题,从一个文件中处理波兰字符。
1.包括<locale.h>
1.并转换您的txt到Ansi -这4个步骤是所有我需要经过大量的研究