Слетает выравнивание для строк с русскими символами. Unicode в Си.
Всех приветствую!
Все таки решил создать новую тему.
Хотя обсуждение началось в этой теме, но там оно больше уклонилось в лингвистическую строну, да и у меня немного другой вопрос.
Проблема вот в чем: если в printf-е мы ставим «%30s», то у нас выводится сначало 30-length(str) пробелов, потом наша строка str длиной length(str).
Но это не работает, если в строке есть не англ. символы.
Чтобы проверить какое будет поведение под Windows и Linux, а также с char* и wchar_t*, набросал программку:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <locale.h>
#include <wchar.h>
#include <uchar.h> // for char16_t
char *str_dup(char const *in)
{
size_t len = strlen(in);
char *out = malloc(len+1);
strncpy(out, in, len+1);
return out;
}
wchar_t *wstr_dup(wchar_t const *in)
{
size_t len = wcslen(in);
wchar_t *out = malloc((len+1)* sizeof(wchar_t));
wcsncpy(out, in, (len+1) );
return out;
}
wchar_t* convert_to_wstr(const char* cstr)
{
mbstate_t state;
memset(&state, 0, sizeof(state) );
size_t out_sz = 1 + mbsrtowcs(NULL, &cstr, 0, &state);
wchar_t* ws = malloc(out_sz*sizeof(wchar_t) );
mbstowcs(ws, cstr, out_sz);
return ws;
}
char* convert_to_cstr(const wchar_t* wstr)
{
mbstate_t state;
memset(&state, 0, sizeof(state) );
size_t out_sz = 1 + wcsrtombs(NULL, &wstr, 0, &state);
char* cs = malloc(out_sz*sizeof(char) );
wcstombs(cs, wstr, out_sz);
return cs;
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
typedef struct CSTRUCT
{
char* data_only_engl; // только англ. буквы
char* data_from_file; // строка из файла
char* data_from_prog; // строка в программе
} CSTRUCT;
CSTRUCT* cs_create(const char* cstr)
{
CSTRUCT* cs = malloc(sizeof(CSTRUCT));
cs->data_only_engl = str_dup("Hello Friend");
cs->data_from_prog = str_dup(cstr);
FILE *input = fopen("text.txt", "r");
char buff[128];
memset(buff, '\0', 128);
fgets(buff, 128, input);
cs->data_from_file = str_dup(buff);
return cs;
}
void cs_print(CSTRUCT* cs)
{
fprintf(stdout,"------------------------------|\n");
const char FMT[] = "%30s| %2ld\n";
fprintf(stdout, FMT, cs->data_only_engl, strlen(cs->data_only_engl) );
fprintf(stdout, FMT, cs->data_from_prog, strlen(cs->data_from_prog) );
fprintf(stdout, FMT, cs->data_from_file, strlen(cs->data_from_file) );
fprintf(stdout,"123456789012345678901234567890|\n"); // ровно 30 символов
}
void cs_free(CSTRUCT** cs)
{
free( (*cs)->data_from_prog);
free( (*cs)->data_from_file);
free( (*cs)->data_only_engl);
free(*cs);
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
typedef struct WSTRUCT
{
wchar_t* data_only_engl;
wchar_t* data_from_file;
wchar_t* data_from_prog;
} WSTRUCT;
WSTRUCT* ws_create(const char* str)
{
WSTRUCT* ws = malloc(sizeof(WSTRUCT));
ws->data_only_engl = wstr_dup(L"Hello Friend");
ws->data_from_prog = convert_to_wstr(str);
FILE *input = fopen("text.txt", "r");
char buff[128];
memset(buff, '\0', 128);
fgets(buff, 128, input);
ws->data_from_file = convert_to_wstr(buff);
return ws;
}
void ws_print(WSTRUCT* ws)
{
fprintf(stdout,"------------------------------|\n");
const char FMT[] = "%30ls| %2ld\n";
fprintf(stdout, FMT, ws->data_only_engl, wcslen(ws->data_only_engl) );
fprintf(stdout, FMT, ws->data_from_prog, wcslen(ws->data_from_prog) );
fprintf(stdout, FMT, ws->data_from_file, wcslen(ws->data_from_file) );
fprintf(stdout,"123456789012345678901234567890|\n");
}
void ws_free(WSTRUCT** ws)
{
free( (*ws)->data_from_prog);
free( (*ws)->data_from_file);
free( (*ws)->data_only_engl);
free(*ws);
}
int main(int argc, char const *argv[])
{
#if defined (_WIN32)
setlocale(LC_ALL, "ru_RU");
#else
setlocale(LC_ALL, "ru_RU.utf8");
#endif
printf("sizeof(char) = %ld\n", sizeof(char) );
printf("sizeof(char16_t)= %ld\n", sizeof(char16_t) );
printf("sizeof(wchar_t) = %ld\n", sizeof(wchar_t) );
printf("\n");
CSTRUCT* cs = cs_create("Hello! Друг");
cs_print(cs);
cs_free(&cs);
WSTRUCT* ws = ws_create("Hello! Друг");
ws_print(ws);
ws_free(&ws);
return EXIT_SUCCESS;
}
Кратко: есть две структуры одна хранит строки в char*, другая - wchar_t*.
Обе хранят три строки: с чисто англ. символами, со строкой инициализируемой в программе и строкой считываемой из файла:
text.txt
Привет friend из файла!
В итоге под Linux получаю
sizeof(char) = 1
sizeof(char16_t)= 2
sizeof(wchar_t) = 4
------------------------------|
Hello Friend| 12
Hello! Друг| 15
Привет friend из файла!
| 37
123456789012345678901234567890|
------------------------------|
Hello Friend| 12
Hello! Друг| 11
Привет friend из файла!
| 24
123456789012345678901234567890|
Видно, что выравнивание сработало для строки только с англ. символами.
А хотелось бы:
------------------------------|
Hello Friend| 12
Hello! Друг| 11
Привет friend из файла!| 23
123456789012345678901234567890|
Соответственно под Windows имею:
sizeof(char) = 1
sizeof(char16_t)= 2
sizeof(wchar_t) = 2
------------------------------|
Hello Friend| 12
Hello! Друг| 15
Привет friend из файла!
| 37
123456789012345678901234567890|
------------------------------|
Hello Friend| 12
Hello! Друг| 15
Привет friend из файла!
| 37
123456789012345678901234567890|
Как сделать кроссплатф. вывод на экран с правильным выравниваем?