私のセットアップ:gcc-4.9.2、UTF-8環境。
次のCプログラムはASCIIで機能しますが、UTF-8では機能しません。
入力ファイルを作成します。
echo -n 'привет мир' > /tmp/вход
これは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);
}
結果を確認します。
./test; grep -q ' привет мир' /tmp/выход && echo OK
UTF-8コードをあたかもASCIIコード-シンボルのバイト数をわざわざにしないなど)のように機能させるために何をする必要があります。 UTF-8シンボルを単一のユニット(argv、STDIN、STDOUT、STDERR、ファイルの入力、出力、プログラムコードを含む)として扱いますか?
#define SIZE 10
バッファサイズ10は、UTF-8文字列を格納するには不十分ですпривет мир
。より大きな値に変更してみてください。私のシステム(Ubuntu 12.04、gcc 4.8.1)では、20に変更すると完全に機能しました。
UTF-8は、文字ごとに1〜4バイトを使用するマルチバイトエンコーディングです。したがって、上記のバッファサイズとして40を使用する方が安全です。 1つのUnicode文字は何バイト必要ですか? で大きな議論がありますが、これは興味深いかもしれません。
Siddhartha Ghosh の answer は基本的な問題を与えます。ただし、コードを修正するにはさらに作業が必要です。
次のスクリプト(chk-utf8-test.sh
)を使用しました。
echo -n 'привет мир' > вход
make utf8-test
./utf8-test
grep -q 'привет мир' выход && echo OK
私はあなたのプログラムを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);
}
そして、スクリプトを実行すると、次のようになりました。
$ 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
$
記録のために、Mac OS X 10.10.3でGCC 5.1.0を使用していました。
これは他の答えの結果の帰結ですが、これを少し異なる角度から説明しようと思います。
以下は、Jonathan Lefflerのコードのバージョンで、3つのわずかな変更があります。(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);
}
正の数値幅指定子を使用したsprintf
の動作は、左からスペースを埋め込むことであるため、使用しようとしたスペースは不要です。ただし、パディングを実際に実行するには、ターゲットフィールドが印刷する文字列よりも広いことを確認する必要があります。
この答えを自己完結させるために、他の人がすでに言ったことを繰り返します。従来のchar
は常に正確に1バイトですが、UTF-8の1文字は通常、すべての文字が実際にASCIIである場合を除いて、正確に1バイトではありません。 UTF-8の魅力の1つは、レガシーCコードがUTF-8について何も知らなくても動作し続けることですが、もちろん、1つの文字が1つのグリフであるという仮定は成り立ちません。 (たとえば、ご覧のとおり、「приветмир」のグリフпは2バイトにマップされます。したがって、2つのchar
s-"\320\277"
。)
これは明らかに理想的ではありませんが、コードがグリフのセマンティクスを特に気にしない場合は、UTF-8を「単なるバイト」としてcanで処理できることを示しています。もしそうなら、概説したようにwchar_t
に切り替える方が良いでしょう。ここ: http://www.gnu.org/software/libc/manual/html_node/Extended-Char-Intro.html
ただし、標準の期待値がUTF-8の場合、標準のwchar_t
は理想的ではありません。例参照 GNU libunistring documentation それほど邪魔にならない代替手段と、少しの背景。これにより、char
をuint8_t
に、さまざまなstr*
関数をu8_str*
に置き換えて実行できるようになります。 1つのグリフが1バイトに等しいという前提に対処する必要がありますが、これはサンプルプログラムのマイナーな技術になります。適応は http://ideone.com/p0VfXq で利用可能です(ただし、残念ながらライブラリは http://ideone.com/ では利用できないため、実証できません)そこ)。
おそらくあなたのtest.c
ファイルはUTF-8形式で保存されていないため、「приветмир」文字列はASCII-および比較に失敗しました。ソースファイルのテキストエンコーディングを変更して、再試行してください。