我的设置:gcc-4.9.2,UTF-8环境.
以下C程序以ASCII格式运行,但不以UTF-8格式运行.
创建输入文件:
echo -n '?????? ???' > /tmp/????
Run Code Online (Sandbox Code Playgroud)
这是test.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 10
int main(void)
{
char buf[SIZE+1];
char *pat = "?????? ???";
char str[SIZE+2];
FILE *f1;
FILE *f2;
f1 = fopen("/tmp/????","r");
f2 = fopen("/tmp/?????","w");
if (fread(buf, 1, SIZE, f1) > 0) {
buf[SIZE] = 0;
if (strncmp(buf, pat, SIZE) == 0) {
sprintf(str, "% 11s\n", buf);
fwrite(str, 1, SIZE+2, f2);
}
}
fclose(f1);
fclose(f2);
exit(0);
}
Run Code Online (Sandbox Code Playgroud)
检查结果:
./test; grep -q ' ?????? ???' /tmp/????? && echo OK
Run Code Online (Sandbox Code Playgroud)
应该怎样做才能使UTF-8代码像ASCII代码一样工作 - 不要打扰符号占用的字节数等等.换句话说:在示例中要将任何UTF-8符号视为单个unit(包括argv,STDIN,STDOUT,STDERR,文件输入,输出和程序代码)?
Sid*_*osh 10
#define SIZE 10
Run Code Online (Sandbox Code Playgroud)
缓冲区大小为10不足以存储UTF-8字符串?????? ???.尝试将其更改为更大的值.在我的系统(Ubuntu 12.04,gcc 4.8.1)上,将其更改为20,工作得很好.
UTF-8是一种多字节编码,每个字符使用1到4个字节.因此,使用40作为上面的缓冲区大小更安全.关于一个Unicode字符占用多少字节有一个很大的讨论?这可能很有趣.
Siddhartha Ghosh的回答为您提供了基本问题.但是,修复代码需要更多工作.
我使用了以下脚本(chk-utf8-test.sh):
echo -n '?????? ???' > ????
make utf8-test
./utf8-test
grep -q '?????? ???' ????? && echo OK
Run Code Online (Sandbox Code Playgroud)
我打电话给你的程序,utf8-test.c并像这样修改了源代码,删除了引用/tmp,并且对长度更加小心:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 40
int main(void)
{
char buf[SIZE + 1];
char *pat = "?????? ???";
char str[SIZE + 2];
FILE *f1 = fopen("????", "r");
FILE *f2 = fopen("?????", "w");
if (f1 == 0 || f2 == 0)
{
fprintf(stderr, "Failed to open one or both files\n");
return(1);
}
size_t nbytes;
if ((nbytes = fread(buf, 1, SIZE, f1)) > 0)
{
buf[nbytes] = 0;
if (strncmp(buf, pat, nbytes) == 0)
{
sprintf(str, "%.*s\n", (int)nbytes, buf);
fwrite(str, 1, nbytes, f2);
}
}
fclose(f1);
fclose(f2);
return(0);
}
Run Code Online (Sandbox Code Playgroud)
当我运行脚本时,我得到了:
$ bash -x chk-utf8-test.sh
+ '[' -f /etc/bashrc ']'
+ . /etc/bashrc
++ '[' -z '' ']'
++ return
+ alias 'r=fc -e -'
+ echo -n '?????? ???'
+ make utf8-test
gcc -O3 -g -std=c11 -Wall -Wextra -Werror utf8-test.c -o utf8-test
+ ./utf8-test
+ grep -q '?????? ???' $'??\213?\205??'
+ echo OK
OK
$
Run Code Online (Sandbox Code Playgroud)
为了记录,我在Mac OS X 10.10.3上使用GCC 5.1.0.
这更像是其他答案的推论,但我会尝试从稍微不同的角度解释这一点。
这是 Jonathan Leffler 的代码版本,有三个细微的变化:(1)我明确指出了 UTF-8 字符串中的实际单个字节;和(2)我修改了sprintf格式化字符串宽度说明符,希望能做你实际尝试做的事情。同样切向(3)我曾经perror在出现故障时收到稍微有用的错误消息。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE 40
int main(void)
{
char buf[SIZE + 1];
char *pat = "\320\277\321\200\320\270\320\262\320\265\321\202"
" \320\274\320\270\321\200"; /* "?????? ???" */
char str[SIZE + 2];
FILE *f1 = fopen("\320\262\321\205\320\276\320\264", "r"); /* "????" */
FILE *f2 = fopen("\320\262\321\213\321\205\320\276\320\264", "w"); /* "?????" */
if (f1 == 0 || f2 == 0)
{
perror("Failed to open one or both files"); /* use perror() */
return(1);
}
size_t nbytes;
if ((nbytes = fread(buf, 1, SIZE, f1)) > 0)
{
buf[nbytes] = 0;
if (strncmp(buf, pat, nbytes) == 0)
{
sprintf(str, "%*s\n", 1+(int)nbytes, buf); /* nbytes+1 length specifier */
fwrite(str, 1, 1+nbytes, f2); /* +1 here too */
}
}
fclose(f1);
fclose(f2);
return(0);
}
Run Code Online (Sandbox Code Playgroud)
sprintf使用正数宽度说明符的行为是从左侧填充空格,因此您尝试使用的空格是多余的。但是您必须确保目标字段比您正在打印的字符串更宽,以便实际进行任何填充。
为了使这个答案自成一体,我将重复其他人已经说过的内容。传统char的总是一个字节,但 UTF-8 中的一个字符通常不完全是一个字节,除非您的所有字符实际上都是 ASCII。UTF-8 的吸引力之一是遗留 C 代码不需要了解任何关于 UTF-8 的信息就可以继续工作,但当然,一个字符是一个字形的假设是不成立的。(例如,如您所见,“?????? ???”中的字形?映射到两个字节——因此,两个chars -- "\320\277"。)
这显然不太理想,但表明如果您的代码不是特别关心字形语义,您可以将 UTF-8 视为“仅字节”。如果你这样做,你最好切换到wchar_t如这里概述的那样:http : //www.gnu.org/software/libc/manual/html_node/Extended-Char-Intro.html
但是,wchar_t当标准期望是 UTF-8 时,该标准并不理想。请参阅例如GNU libunistring 文档以了解侵入性较小的替代方案和一些背景知识。有了这一点,你应该能够取代char与uint8_t和各种str*与功能u8_str*置换和完成。一个字形等于一个字节的假设仍然需要解决,但这在您的示例程序中成为一个次要的技术问题。http://ideone.com/p0VfXq提供了改编版本(尽管遗憾的是该库在http://ideone.com/上不可用,因此无法在那里演示)。
| 归档时间: |
|
| 查看次数: |
31953 次 |
| 最近记录: |