JFP 開発ガイド

プログラム例

ここでは、ワイド文字列操作 API を使用したプログラム例を 2 つ紹介します。ワイド文字列操作 API を使用する場合は、wchar.h ヘッダーファイルを取り込み、処理の最初の段階で setlocale() を呼び出して、動作ロケールを適切に設定する必要があります。

例 2-2 では、ワイド文字列を比較しています。例 2-1 で、複数バイト表現の例として strxfrm()strcmp() を使って strcoll() を簡単にシミュレートしましたが、ここではワイド文字表現の場合のプログラミング例を紹介します。

例 2-2 では、main() 関数から my_wcscoll() を呼び出していますが、システムが提供する wcscoll() を呼び出すように変更してもまったく同じ結果が得られます。


例 2-2 ワイド文字列の比較

sun% cat my_wcscoll.c
/*
 * Read lines from two files, and return the
 * order that is the same as they are compared
 * by wcscoll().
 * Comparing will stop if either file reaches EOF.
 * It is assumed that each line has at most BUFSIZ - 1
 * wide char length.
 *
 * Actual processing is done by my_wcscoll(), which
 * does the followings.
 *	1.	Call wcsxfrm() to get the size of
 *		transformed wide string.
 *	2.	Dynamically allocate the memory the
 *		buffer. It will be big enough to contain
 *		the transformed wide string and terminating (wchar_t)NULL.
 *	3.	Call wcsxfrm() again to get
 *		the transformed wide string. To verify if
 *		the error happens, it must clear `errno'
 *		then call wcsxfrm(). After that, check
 *		the value of `errno.'
 *	4.	Call wcscmp() with the transformed wide strings.
 *		Since these strings are artificialy created,
 *		they are not allowed to display.
 */

#include <stdio.h>
#include <locale.h>
#include <wchar.h>
#include <stdlib.h>
#include <errno.h>

static int my_wcscoll(const wchar_t *, const wchar_t *);

int
main(int argc, char *argv[])
{
	FILE *fp1, *fp2;
	wchar_t buf1[BUFSIZ], buf2[BUFSIZ];
	wchar_t*wcp1, *wcp2;
	int retval;

	setlocale(LC_ALL, "");
if (argc != 3) {
		fprintf(stderr, "¥tUsage: %s file1 file2¥n", argv[0]);
		exit(-1);
	}
	fp1 = fopen((const char *)argv[1], "r");
	fp2 = fopen((const char *)argv[2], "r");
	if ((fp1 == (FILE *)NULL) || (fp2 == (FILE *)NULL)) {
		fprintf(stderr, "%s: Couldn't open %s ¥n",
			argv[0],
			((fp1 == (FILE *)NULL) ? argv[1] : argv[2]));
		exit(-1);
	for (;;) {
		wcp1 = fgets(buf1, BUFSIZ, fp1);
		wcp2 = fgets(buf2, BUFSIZ, fp2);
		if ((wcp1 == (wchar_t)NULL) && (wcp2 == (wchar_t)NULL)) {
			exit(0);
		} else if ((wcp1 == (wchar_t)NULL) || (wcp2 == (wchar_t)NULL)) {
			fprintf(stderr, "%s: No more contents in %s¥n",
					argv[0], (wcp1 ? argv[2] : argv[1]));
			exit(0);
		}
		retval = my_wcscoll((const wchar_t *)buf1, (const wchar_t *)buf2);
		if (retval == 0) {
			fprintf(stdout, "The same collation order.¥n");
		} else if (retval > 0) {
			fprintf(stdout,
				"%s is bigger than %s in terms of collation order.¥n",
				argv[1], argv[2]);
		} else {
			fprintf(stdout,
				"%s is bigger than %s in terms of collation order.¥n",
				argv[2], argv[1]);
		}
	}
	return (0);
}

static int
my_wcscoll(const wchar_t *wcp1, const wchar_t *wcp2)
{
xfrm_len1 = wcsxfrm((wchar_t *)NULL, wcp1, (size_t)0);
	xfrm_len2 = wcsxfrm((wchar_t *)NULL, wcp2, (size_t)0);
	transform_1 = (wchar_t *)malloc((xfrm_len1 + 1) * sizeof(wchar_t));
	transform_2 = (wchar_t *)malloc((xfrm_len2 + 1) * sizeof(wchar_t));

	errno = 0;

	wcsxfrm(transform_1, wcp1, (xfrm_len1 + 1));
	if (errno != 0) {
		perror("my_wcscoll(): Error in transforming 1st string");
		exit(-1);
	}
	wcsxfrm(transform_2, wcp2, (xfrm_len2 + 1));
	if (errno != 0) {
		perror("my_wcscoll(): Error in transforming 2nd string");
		exit(-1);
	}
	ret_coll =
		wcscmp((const wchar_t *)transform_1, (const wchar_t *)transform_2);

	free(transform_1);
	free(transform_2);

	return (ret_coll);
}
sun% cat file1
入力サンプル 1 です。
This line is identical.
短いです。
sun% cat file2
入力サンプル 2 です。
This line is identical.
こちらの行は長くなっています。
sun% cc -o my_wcscoll my_wcscoll.c
sun% ./my_wcscoll file1 file2
file2 is bigger than file1 in terms of collation order.
The same collation order.
file1 is bigger than file2 in terms of collation order.
./my_wcscoll: No more contents in file1	

例 2-3 は、2 つのファイルを読み込んで、2 段組にして表示する例です。表示の際は、1 行あたり 80 カラムを最大とし、入力ファイルの行末が均等に削除されます。複数バイト表現の途中で文字列が分断されないように調整するために、ワイド文字列表現にして処理します。


例 2-3 ワイド文字列の比較 (2 段組表示)

sun% cat my_pr.c
/*
 * Read lines from two files and merge them into
 * one line so that it doesn't exceed 80 columns.
 * When either file reaches EOF, empty lines will be
 * shown.
 * It is assumed each line has at most BUFSIZ - 1 byte chars
 * and, ASCII space (` `) and vertical bar (`|') character have
 * one display column width.
 * Open files and read lines as wide strings by fgetws().
 * Then call my_pr().
 */

#include <stdio.h>
#include <locale.h>
#include <wchar.h>
#include <stdlib.h>

#define		OUTBUFSIZ					80
#define		LIMIT					((OUTBUFSIZ - 3) / 2)
#define		WSPC_STR					L" "
#define		WSPC_CHR					L' `
#define		WSEPARATOR					L" | "
#define		WTAB					L'¥t'
#define		TABS					8

static void my_pr(wchar_t *, const wchar_t *, const wchar_t *);
static void put_spc(wchar_t *, int *);

int
main(int argc, char *argv[])
{
	FILE *fp1, *fp2;
	wchar_t buf1[BUFSIZ], buf2[BUFSIZ];
	wchar_t outbuf[(OUTBUFSIZ + 1)] = { 0x0 };
	int retval;

	setlocale(LC_ALL, "");

	if (argc != 3) {
		fprintf(stderr, "¥tUsage: %s file1 file2¥n", argv[0]);
		exit(-1);
	}
fp1 = fopen((const char *)argv[1], "r");
	fp2 = fopen((const char *)argv[2], "r");
	if ((fp1 == (FILE *)NULL) || (fp2 == (FILE *)NULL)) {
		fprintf(stderr, "%s: Couldn't open %s ¥n",
				argv[0],
				((fp1 == (FILE *)NULL) ? argv[1] : argv[2]));
		exit(-1);
	}
	for (;;) {
		if (fgetws(buf1, BUFSIZ, fp1) == (wchar_t *)NULL) {
			buf1[0] = `¥0';
		}
		if (fgetws(buf2, BUFSIZ, fp2) == (wchar_t *)NULL) {
			buf2[0] = `¥0';
		}
		if ((buf1[0] == `¥0') && (buf2[0] == `¥0')) {
			return(0);
		}
		my_pr(outbuf, (const wchar_t *)buf1, (const wchar_t *)buf2);
		fprintf(stdout, "%S¥n", outbuf);
		outbuf[0] = `¥0';
	}

	return(0);
}

static void
put_spc(wchar_t *outbufp, int *total)
{
	wcsncat(outbufp, WSPC_STR, 1);
	*total += wcwidth(WSPC_CHR);
}

static void
my_pr(wchar_t *outbufp, const wchar_t *wcp1, const wchar_t *wcp2)
{
	int collen;
	int subtotal1, subtotal2;

for (subtotal1 = 0;
		(collen = wcwidth(*wcp1)) <= (LIMIT - subtotal1); wcp1++) {
		if (collen == -1) {
			if (*wcp1 == WTAB) {
				while ((subtotal1 < LIMIT) &&
					(subtotal1 % TABS) != 1) {
					put_spc(outbufp, &subtotal1);
				}
			} else {
				continue;
			}
		} else if (collen == 0) {
			break;
		} else {
			wcsncat(outbufp, wcp1, 1);
			subtotal1 += collen;
		}
	}

	while (subtotal1 < LIMIT) {
		put_spc(outbufp, &subtotal1);
	}
	wcscat (outbufp, WSEPARATOR);
	subtotal1 += wcswidth(WSEPARATOR, wcslen(WSEPARATOR));

	for (subtotal2 = 0;
		(collen = wcwidth(*wcp2)) <= (LIMIT - subtotal2); wcp2++) {
		if (collen == -1) {
			if (*wcp2 == WTAB) {
				while ((subtotal2 < LIMIT) &&
					(subtotal2 % TABS) != 1) {
					put_spc(outbufp, &subtotal2);
				}
			} else {
				continue;
			}
		} else if (collen == 0) {
			outbufp[(subtotal1 + subtotal2)] = (wchar_t)NULL;
			break;
		} else {
			wcsncat(outbufp, wcp2, 1);
			subtotal2 += collen;
		}
	}

	return;
}
sun% cat file
01234567890123456789012345678901234567890123456789012345678901234567890123456789
0123456789012345678901234567890123456789
アイウエオカキクケコアイウエオカキクケコアイウエオカキクケコアイウエオカキクケコ
アイウエオカキクケコアイウエオカキクケコアイウエオカキクケコアイウエオカキクケコ
010ア010ア010ア010ア010ア010ア010ア010ア010ア010ア010ア010ア010
ア010ア010ア010ア
sun% cat file1
入力サンプル 1 です。
This line is identical.
短いです。
sun% cc -o my_pr my_pr.c
sun% ./my_pr file file1
01234567890123456789012345678901234567	| 入力サンプル 1 です。
0123456789012345678	| This line is identical.
アイウエオカキクケコアイウエオカキクケコアイウエオカキクケコアイウエオカキク	| 短いです。
010ア010ア010ア010ア010ア010ア010ア01	|