LINUX.ORG.RU

[C] Проблема с towlower и кодировками.

 


0

1

Есть файл в UTF-8 с русскими буквами. В исходнике он открывается, содержимое перекодируется в UCS-4 ( для того, чтобы можно было работать с wide char функциями ), все символы переводятся в нижний регистр, содержимое перекодируется назад UTF-8 ( без этого выводятся вопросики ) и выводится на экран.

Проблема: Символы не переводятся в нижний регистр.

#include <stdio.h>
#include <locale.h>
#include <wchar.h>
#include <wctype.h>
#include <errno.h>
#include <iconv.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

static int
convert( const char *from, const char *to,
		char * const input, size_t isize,
		char **output )
{
#define OUTBUF_SIZE 1024
	int err = -1;
	
	iconv_t idesc = iconv_open( to, from );
	if ( idesc == ( iconv_t ) -1 )
	{
		return -1;
	}

	char *out = NULL;
	char *outptr = NULL;
	char *in = input;
	size_t buflen = 0;
	size_t outmaxlen = 0;
	
	while ( isize > 0 ) {
		int n;
		
		/* Increase the buffer.  */
		char *new_outp = ( char * ) realloc( out, outmaxlen + OUTBUF_SIZE );
		if ( new_outp == NULL )
		{
			free( out );
			goto out1;
		}
		
		outptr = ( outptr - out ) + new_outp;
		
		out = new_outp;
		outmaxlen += OUTBUF_SIZE;
		
		buflen = OUTBUF_SIZE;
		
		n = iconv( idesc, &in, &isize, &outptr, &buflen );
		
		if ( n == ( size_t ) -1 && errno != E2BIG )
		{
			free( out );
			goto out1;
		}
		
	}
	
	err = 0;
	
	*output = out;
	
out1:	
	iconv_close( idesc );
out:
	return err;
#undef OUTBUF_SIZE
}

static void
wcslwr( wchar_t * const str )
{
	size_t i;
	const int len = wcslen( str );
	
	for( i = 0; i < len; ++i ) {
		str[i] = towlower( str[i] );
	}
}

int
main( int argc, char *argv[] )
{
	char *input;
	char *input_name;
	char *tmp;
	wchar_t *nstr;
	struct stat sb;
	int err = EXIT_FAILURE;
	
	if ( argc == 1 )
	{
		return 0;
	}
	
	setlocale( LC_ALL, "" );
	
	//printf( "%s\n", setlocale( LC_ALL, NULL )  );
	
	input_name = argv[1];
	int fdi;
	
	fdi = open( input_name, O_RDONLY );
	
	if ( fdi == -1 )
	{
		perror("open");
		exit( EXIT_FAILURE );
	}

	if ( fstat( fdi, &sb ) == -1 )
	{
		perror( "fstat" );
		goto out1;
	}
	
	size_t fisize = sb.st_size;
	
	input = ( char * )
			mmap( NULL, fisize, PROT_READ, MAP_PRIVATE, fdi, 0 );
	if ( input == NULL )
	{
		perror( "mmap" );
		goto out1;
	}
	
	if ( convert( "UTF-8", "UCS-4", input, fisize, &tmp ) ) {
		perror( "convert" );
		goto out2;
	}
	
	nstr = ( wchar_t * ) tmp;
	
	wcslwr( nstr );
	
	convert( "UCS-4", "UTF-8", ( char * ) nstr, wcslen( nstr ) * sizeof( wchar_t ), &tmp );
	
	int val = fwide( stdout, 0 );
	if ( val == 0 ) {
		if ( fwide( stdout, 1 ) <= 0 ) {
			printf( "%s\n", "Could not switch to wide char mode!" );
			goto out3;
		} else {
			wprintf( L"%s\n", ( wchar_t * ) tmp );
		}	
	} else if ( val > 0 ) {
		wprintf( L"%s\n", ( wchar_t * ) tmp );
	} else {
		printf( "%s\n", "Could not switch to wide char mode!" );
		goto out3;
	}
	
	err = EXIT_SUCCESS;
	
out3:
	free( nstr );
	free( tmp );
out2:
	munmap( input, fisize );
out1:
	close( fdi );
	
	exit( err );
}

Пример работы:

dimorphus@codifier ~/workspace/decode/test $ cat ./test.txt 
АБВГДеежз
ABCDefgdimorphus@codifier ~/workspace/decode/test $ ./a.out ./test.txt 
АБВГДеежз
ABCDefg

Примечание1:

dimorphus@codifier ~/workspace/decode/test $ locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC=C
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

Примечание2:
На мой взгляд в функциях wprintf нужно использовать L"%ls\n", но в таком случае выводятся вопросики.

Вопрос:
Как заставить работать ( переводить в нижний регистр и выводить на экран буквы )?
[Если кто знает] Как подружить gdb и wchar_t (не только английский алфавит) ? [/Если кто знает]



Последнее исправление: dimorphus (всего исправлений: 1)

Из этого примера[1] следует, что нужно указывать «WCHAR_T» вместо «UCS-4» для перекодирования в wchar_t. А для вывода результата по всей видимости достаточно обычного printf(«%s\n»), он ведь уже в мультибайтовом UTF-8, а не в wide char.

[1]: http://www.gnu.org/s/libc/manual/html_node/iconv-Examples.html

Deleted
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.