தமிழ் சொற்களை C நிரலாக்கதின் மூலம் எப்படி அகரவரிசை படுத்துவது?

C நிரலாக்கதில் போது ஒருவேளை UTF-8 ஒருங்குறியில் உள்ள தமிழ்ச்சொற்களை அகரவரிசை படுத்தவோ அல்லது ஒப்பீடு செய்யவோ ஒரு தேவையிருப்பின் அதனை எப்படி செய்வது அல்லது அதற்கென்று ஏதாவது C Library உள்ளதா என்று இந்த பதிவில் காண்போம்.

1 Like

UTF-8 ஒருங்குறியில் உள்ள தமிழ் எழுத்துகளை C நிரலி மூலம் செயலாக்கம் புரிவதற்கென ஒரு C Library உருவாக்கியிருந்தேன்.

அதற்கான Github இணைப்பு: GitHub - deepak-shanmugam/utf_8_tamil_module: C library to primarily handle the tamil unicode letters in utf-8 format.

இதில் int utf_8_ta_compare(const char *first, const char *second); என்னும் function ஐப் பயன்படுத்தி எளிதாக ஒப்பீடு செய்து வரிசைபடுத்த இயலும்.

அந்த function அனுப்பும் எண் negative ஆக இருந்தால் first சொல் அகரவரிசைப்படி முதலில் உள்ளது என்று பொருள். அதுவே positive ஆக இருந்தால் second சொல் அகரவரிசையில் முதலில் வரும் என்று பொருள். அதுவே கிடைப்பது 0 ஆக இருந்தால், first மற்றும் second சொல் இரண்டும் ஒரே சொல் என்று பொருள்.

இதை வைத்து எளிதாக தமிழ்ச்சொற்களை அகரவரிசைப்படுத்தலாம்.

திருத்தம் 1: int utf_8_ta_compare() என்னும் function ஐ utf_tamil.c மற்றும் utf_tamil.h கோப்பில் காணலாம்.

திருத்தம் 2: இது நேரடியாக UTF-8 முறையில் உள்ள சொற்களை (string) செயலாக்கம் புரியும் படி அமைந்துள்ளது குறிப்பிடத்தக்கது. எனவே ஒரே சொல்லில் (string) பல மொழியின் வரியுருக்கள் இருந்தாலும் ஒப்பிடும்போது சொல்லின் வேறுபாட்டை உணர்த்தும். தமிழையும் மற்றவைகளையும் வேறுபடுத்தும். தமிழ் வரியுருவை ஒப்பிடும்போது அகரவரிசை சரியாக இருக்கும். ஆனால் பிற மொழியின் வரியுருவையும் ஒப்பிடும்போது அம்மொழிகளின் உண்மையான அகரவரிசையை இதில் எதிர்பார்க்க இயலாது. மாறாக பிறமொழிகளின் வரியுருக்கள் (code points) எந்த வரிசையில் உள்ளதோ அவ்வரிசையையே ஒப்பீடு தரவரிசையாய் பின்பற்றும்.

நிரலை பகிர்ந்தமைக்கு நன்றி, உங்கள் நிரலை இப்படி இயக்கி பார்த்தேன்

int main(int argc, char *argv[]) {
    char *str1 = "க்";
    char *str2 = "க";

    printf("return value of comparison: %d\n", utf_8_ta_compare(str1, str2));
    return 0;
} 

இதற்கு விடை

return value of comparison: 1

இப்படி வந்தது, ஆனால் க் மெய்யெழுத்து உயிர்மெய் எழுத்தை விட சிறியதுதானே, எனவே விடை -1 என்றுதானே இருக்க வேண்டும்?

மேலும் ஏன் utf-8 வைத்தே ஒப்பிடுகின்றீர்கள்? wchar_t என்று C ல் யுனிகோட் தரவுகளுக்கு தனியாக ஒரு தரவு வகை (Data Type) உள்ளதே? அதை பயன்படுத்தலாமே?

1 Like

அகரவரிசைப்படி பார்த்தால், ‘படம், படும், பட்டம்’ என்பதை

  1. பட்டம்
  2. படம்
  3. படும்

என்ற வரிசையில் தான் வரவேண்டும் என்பது எனது கருத்து.
ட்->ட->டா->டி->டு->டெ->டை->டொ->டோ->டௌ
என்பது தான் அகரவரிசை. இதுகுறித்து ஏதேனும் மாற்றுக்கருத்து இருப்பின் தெரிவிக்கலாம்.
நான் மேற்கூறிய படி பாருங்கள் இது சரியாகவே வேலைசெய்யும்.

திருத்தம்: wchat_t என்ற தரவுவகையை நான் அப்போது அறிந்திருக்கவில்லை. மேலும் அவ்வகையையும் ஒருமுறை ஆய்வு செய்து பார்க்க வேண்டும். மேலும் அந்த தரவுவகையைப் பயன்படுத்தி தமிழ் எழுத்துகளின் ஒப்பீடு மற்றும் அகரவரிசை சரியாக வருகிறதா என்றும் பார்த்துவிட்டு இங்கு மேற்கொண்டு பதிவிடுகிறேன்.

நீங்கள் கூறுவது வைத்தே str1 = "பட்’’ மற்றும் str2 = “பட” என்று கொடுத்தால் 1 என்றுதான் வருகின்றது. என்னுடைய புரிதல் படி -1 என்று வந்தால் அகர வரிசையில் உள்ளது என்று என்னுகிறேன்.

1 Like

ஆய்வு செய்தமைக்கு மிக்க நன்றி. இதில் ஒரு இடத்தில் மட்டும் கோளாறு / பிழை ஏற்படுவதை தற்போது உங்கள் மூலம் அறிந்து கொண்டேன்.

கோளாறு விளக்கம்: அதாவது ஒரு சொல்லின் இறுதியில் அகர உயிர்மெய் வந்து அதனுடன் ஒப்பிடப்படும் மற்ற சொல்லில் அதே இணமான மெய்யுடன் ஒப்பீடு நிகழ்ந்தால் இப்பிழை ஏற்படுகிறது. ஆனால் முதலிலும் இடையிலும் வரும் அகர உயிர்மெய் இதுபோன்ற ஒப்பீட்டில் இப்பிழையை ஏற்படுத்துவதில்லை.

ட = ட + ‘\0’ மற்றும் ட் = ட + ் + ‘\0’ என்ற ஒப்பீட்டு சூழலில் ‘\0’ ற்கு முன்பு வரும் அகர உயிர்மெய் (ட) அதன் இணமான மெய்யுடன் (ட்) ஒப்பீட்டை முழுமையாக முடிக்காததால் இக்கோளாறு ஏற்படுகிறது. நான் தான் முதலில் நீங்கள் சுட்டிக்காட்டியதை சரியாக நோக்கவில்லை. இப்பிழையை இன்றே சரிசெய்து விடுகிறேன். -நன்றி

மோகன், இக்கோளாறு தற்போது சரிசெய்யப்பட்டது. இனி அனைத்து இடங்களிலும் சரியாக ஒப்பீடு செய்யும். அதனால் முறையான அகரவரிசையில் இருக்கும்.

image

இப்பிழை / கோளாறை கண்டுபிடித்தமைக்கு நன்றி. எப்படி இதனை ஆய்வு செய்யும்போது தவறினேன் என்று தெரியவில்லை. ஆனால் தற்போது சரிசெய்யப்பட்டது. -நன்றி

1 Like

wchar_t வைத்து எழுதப்பட்ட தமிழ்ச்சொல் ஒப்பீட்டு கருவி

/*
 * Simple Commandline tool to compare two tamil strings
 *
 * Copyright: 2024-Present
 * Author: Mohan Raman <mohan43u@gmail.com>
 * License: GPLv2
 *
 * Compile:
 * gcc -o tamil_strcmp tamil_strcmp.c -lm
 *
 * Run:
 * ./tamil_strcmp தமிழ் தமிழ்
 */

#include <locale.h>
#include <stdio.h>
#include <string.h>
#include <wchar.h>
#include <stdint.h>
#include <stdlib.h>
#include <errno.h>
#include <stdbool.h>
#include <math.h>

int32_t empty[] = {
	-1,
};

int32_t numbers[] = {
	0x0be6, // ௦
	0x0be7, // ௧
	0x0be8, // ௨
	0x0be9, // ௩
	0x0bea, // ௪
	0x0beb, // ௫
	0x0bec, // ௬
	0x0bed, // ௭
	0x0bee, // ௮
	0x0bef, // ௯
	0x0bf0, // ௰
	0x0bf1, // ௱
	0x0bf2, // ௲
	-1,
};

int32_t uyir[] = {
	0x0b85, // அ
	0x0b86, // ஆ
	0x0b87, // இ
	0x0b88, // ஈ
	0x0b89, // உ
	0x0b8a, // ஊ
	0x0b8e, // ஏ
	0x0b8f, // ஏ
	0x0b90, // ஐ
	0x0b92, // ஒ
	0x0b93, // ஓ
	0x0b94, // ஔ
	0x0b83, // ஃ
	-1,
};

int32_t meithunai[] = {
	0x0bcd, // ்
	-1,
};

int32_t uyirmei[] = {
	0x0b95, // க
	0x0b99, // ங
	0x0b9a, // ச
	0x0b9e, // ஞ
	0x0b9f, // ட
	0x0ba3, // ண
	0x0ba4, // த
	0x0ba8, // ந
	0x0baa, // ப
	0x0bae, // ம
	0x0baf, // ய
	0x0bb0, // ர
	0x0bb2, // ல
	0x0bb5, // வ
	0x0bb4, // ழ
	0x0bb3, // ள
	0x0bb1, // ழ
	0x0ba9, // ன
	0x0bbc, // ஼
	0x0b9c, // ஜ
	0x0bb7, // ஷ
	0x0bb8, // ஸ
	0x0bb9, // ஹ
	-1,
};

int32_t thunai[] = {
	0x0bbe, // ா
	0x0bbf, // ி
	0x0bc0, // ீ
	0x0bc1, // ு
	0x0bc2, // ூ
	0x0bc6, // ெ
	0x0bc7, // ே
	0x0bc8, // ை
	0x0bca, // U+0BCA
	0x0bcb, // ோ
	0x0bcc, // ௌ
	0x0bd7, // ௗ
	0x0b82, // ஂ
	-1,
};

int32_t special[] = {
	0x0bd0, // ௐ
	0x0bf3, // ௳
	0x0bf4, // ௴
	0x0bf5, // ௵
	0x0bf6, // ௶
	0x0bf7, // ௷
	0x0bf8, // ௸
	0x0bf9, // ௹
	0x0bfa, // ௺
	-1,
};

int32_t *weightmap[] = {
	empty,
	numbers,
	uyir,
	meithunai,
	uyirmei,
	thunai,
	special,
	NULL,
};

int32_t find_indexes(const wchar_t *wcsstring,
		     int32_t *index0_in,
		     int32_t *index1_in) {
	int32_t result = -1;
	int32_t index0 = -1;
	int32_t index1 = -1;
	bool stop = false;

	if (wcsstring[0] == L'\0') {
		dprintf(2,
			"empty string\n");
		goto end;
	}

	for (int32_t idx0 = 0;
	     weightmap[idx0] != NULL
		     && stop == false;
	     idx0++) {
		for (int32_t idx1 = 0;
		     weightmap[idx0][idx1] != -1
			     && stop == false;
		     idx1++) {
			if (weightmap[idx0][idx1] == wcsstring[0]) {
				result = 1;
				index0 = idx0;
				index1 = idx1;
				stop = true;
			}
		}
	}
end:
	*index0_in = index0;
	*index1_in = index1;
	return result;
}

int32_t tamil_wchar_len(const wchar_t *wcsstring,
			int32_t *weight_in) {
	int32_t len = -1;
	int32_t weight = -1;
	int32_t index0 = -1;
	int32_t index1 = -1;
	size_t wcsstring_length = -1;

	if (wcsstring[0] == L'\0') {
		dprintf(2,
			"null string\n");
		goto end;
	}

	if ((wcsstring_length = wcslen(wcsstring)) <= 0) {
		dprintf(2,
			"empty string\n");
		goto end;
	}

	if (find_indexes(wcsstring,
			 &index0,
			 &index1) < 0) {
		dprintf(2,
			"failed to find indexes\n");
		goto end;
	}

	if (index0 != 4) {
		len = 1;
		weight = pow(10,index0) + index1;
		goto end;
	}

	if (index0 == 4) {
		if (wcsstring_length <= 1) {
			len = 1;
			weight = pow(10,index0) + index1;
			goto end;
		}

		int32_t prev_index0 = index0;
		int32_t prev_index1 = index1;
		if (find_indexes(wcsstring + 1,
				 &index0,
				 &index1) < 0) {
			len = 1;
			weight = pow(10,prev_index0) + prev_index1;
			goto end;
		}
		/* dprintf(2, */
		/* 	"prev_index0 = %d, prev_index1 = %d, index0 = %d, index1 = %d\n", */
		/* 	prev_index0, */
		/* 	prev_index1, */
		/* 	index0, */
		/* 	index1); */

		if (index0 == 4) {
			len = 1;
			weight = pow(10,prev_index0) + prev_index1;
			goto end;
		}

		if (index0 == 3) {
			len = 2;
			weight = pow(10, index0) + prev_index1;
			goto end;
		}
		if (index0 == 5) {
			len = 2;
			int32_t uyirmei_w = pow(10, index0);
			int32_t thunai_w = 100 * index1;
			weight = uyirmei_w + thunai_w + prev_index1;
			goto end;
		}

		len = 1;
		weight = pow(10,prev_index0) + prev_index1;
	}
end:
	if (weight_in != NULL) {
		*weight_in = weight;
	}
	return len;
}

wchar_t *strtowcs(const char *utf8string) {
	wchar_t *wcsstring = NULL;
	ssize_t wcsstring_len = 0;

	if ((wcsstring = calloc(sizeof(wchar_t),
				strlen(utf8string) + 1)) == NULL) {
		dprintf(2,
			"failed to allocate wcsstring: %s\n",
			strerror(errno));
		goto end;
	}

	if ((wcsstring_len = mbstowcs(wcsstring,
				      utf8string,
				      strlen(utf8string))) < 0) {
		dprintf(2,
			"failed to convert utf8 to wcs: %s\n",
			strerror(errno));
		goto end;
	}
end:
	return wcsstring;
}

bool istamil(const wchar_t wchar) {
	return !(wchar < 0x0b80 ||  wchar > 0x0bff);
}

int32_t tamil_strcmp(const char *string0,
		     const char *string1) {
	int32_t result = -2;
	wchar_t *wcsstring0 = NULL;
	wchar_t *wcsstring1 = NULL;
	int32_t wcsstring0_len = -1;
	int32_t wcsstring1_len = -1;
	int32_t offset0 = 0;
	int32_t offset1 = 0;

	if ((wcsstring0 = strtowcs(string0)) == NULL) {
		dprintf(2,
			"failed to convert %s to wchar_t string: %s\n",
			string0,
			strerror(errno));
		goto end;
	}
	wcsstring0_len = wcslen(wcsstring0);

	if ((wcsstring1 = strtowcs(string1)) == NULL) {
		dprintf(2,
			"failed to convert %s to wchar_t string: %s\n",
			string1,
			strerror(errno));
		goto end;
	}
	wcsstring1_len = wcslen(wcsstring1);

	while(offset0 < wcsstring0_len
	      && offset1 < wcsstring1_len) {
		int32_t len0 = -1;
		int32_t len1 = -1;
		int32_t weight0 = -1;
		int32_t weight1 = -1;
		bool istamil_failed = false;

		if (istamil(wcsstring0[offset0]) == false) {
			wcsstring0[offset0] = L'\0';
			istamil_failed = true;
		}
		if (istamil(wcsstring1[offset1]) == false) {
			wcsstring1[offset1] = L'\0';
			istamil_failed = true;
		}
		if (istamil_failed) {
			break;
		}

		len0 = tamil_wchar_len(wcsstring0 + offset0,
				       &weight0);
		if (len0 < 0) {
			goto end;
		}

		len1 = tamil_wchar_len(wcsstring1 + offset1,
				       &weight1);
		if (len1 < 0) {
			goto end;
		}

		/* dprintf(2, */
		/* 	"wcsstring0[%d] = 0x%04x, weight0 = %d, wcsstring1[%d] = 0x%04x, weight1 = %d\n", */
		/* 	offset0, */
		/* 	wcsstring0[offset0], */
		/* 	weight0, */
		/* 	offset1, */
		/* 	wcsstring1[offset1], */
		/* 	weight1); */

		if (weight0 < weight1) {
			result = -1;
			goto end;
		}

		if (weight0 > weight1) {
			result = 1;
			goto end;
		}

		if (weight0 == weight1) {
			offset0 += len0;
			offset1 += len1;
			result = 0;
			continue;
		}
	}

	wcsstring0_len = wcslen(wcsstring0);
	wcsstring1_len = wcslen(wcsstring1);
	if (wcsstring0_len < wcsstring1_len) {
		result = -1;
		goto end;
	}

	if (wcsstring0_len > wcsstring1_len) {
		result = 1;
		goto end;
	}
	result = 0;
end:
	if (wcsstring0 != NULL) {
		free(wcsstring0);
		wcsstring0 = NULL;
	}

	if (wcsstring1 != NULL) {
		free(wcsstring1);
		wcsstring1 = NULL;
	}
	return result;
}

int32_t main(int32_t argc,
	     char *argv[],
	     char *envp[]) {
	if (argc < 3) {
		dprintf(2,
			"[usage] %s <string1> <string2>\n",
			argv[0]);
		exit(1);
	}

	if(setlocale(LC_CTYPE, "") == NULL) {
		dprintf(2,
			"setlocale failed: %s\n",
			strerror(errno));
	}

	dprintf(1,
		"tamil_strcmp(%s,%s) = %d\n",
		argv[1],
		argv[2],
		tamil_strcmp(argv[1], argv[2]));
	return 0;
}
1 Like

வணக்கம் மோகன், உங்களின் நிரலுக்கு நன்றி, அதில் நான் “1அ” மற்றும் “1ம” ஆகிய சொற்களை ஒப்பீடு செய்தால், எனக்கு அதற்கான விடை 0 என்று கொடுக்கிறது. இதிலிருந்து எனக்கு ஒரு ஐயம் என்னவெனில், wchar_t பயன்படுத்தினால் ஒரு சொல் (string) முழுவதும் ஒரே மொழியில் இருக்கும் வண்ணம் பார்த்துக்கொள்ள வேண்டுமா?

நீங்கள் வழங்கிய நிரல் உள்ளீட்டில் முழுவதும் ஒரே மொழி இருக்கும் வகையில் சொல்லைக் கொடுத்தால் சரியாக வேலை செய்கிறது. இது wchar_t பயன்படுத்துவதால் ஏற்படும் பின்னடைவா? அல்லது நிரலாக்கத்தை மாற்றி செயல்படுத்துவதாலேயே இதில் ஏற்படும் அச்சிக்கலை போக்க இயலுமா?

-நன்றி

அதில் நான் “1அ” மற்றும் “1ம” ஆகிய சொற்களை ஒப்பீடு செய்தால், எனக்கு அதற்கான விடை 0 என்று கொடுக்கிறது

இந்த நிரல் தமிழ் அல்லாத எந்த எழுத்தும் வந்தவுடன் ஒப்பிடுதலை நிறுத்திவிடும்.

இது wchar_t பயன்படுத்துவதால் ஏற்படும் பின்னடைவா? அல்லது நிரலாக்கத்தை மாற்றி செயல்படுத்துவதாலேயே இதில் ஏற்படும் அச்சிக்கலை போக்க இயலுமா?

wchar_t எல்லா மொழிக்கும் பொதுவானது யுனிக்கோடின் அனைத்து எழுத்துருக்களையும் உள்ளடக்கியது, ஆனால் எனது நிரலில் நான் தமிழ் மொழியை மட்டுமே ஒப்பிட வழி செய்துள்ளேன். மற்ற எழுத்துருக்கள் எந்த வரிசையில் வரவேண்டும் என்று தமிழ் மொழியை ஒப்பிடும் நிரலுக்கு அவசியம் இருக்காது என்று நினைக்கிறேன்.