Мне было интересно, безопасно ли это сделать?
wchar_t wide = /* something */;
assert(wide >= 0 && wide < 256 &&);
char myChar = static_cast<char>(wide);
Если я уверен, что широкий char попадет в диапазон ASCII.
Мне было интересно, безопасно ли это сделать?
wchar_t wide = /* something */;
assert(wide >= 0 && wide < 256 &&);
char myChar = static_cast<char>(wide);
Если я уверен, что широкий char попадет в диапазон ASCII.
assert
предназначен для обеспечения того, что что-то истинно в режиме отладки, без какого-либо эффекта в сборке релиза. Лучше использовать оператор if
и иметь альтернативный план для символов, находящихся за пределами диапазона, если только единственный способ получить символы вне диапазона - это ошибка программы.
Кроме того, в зависимости от кодировки символов вы можете найти разницу между символами Unicode от 0x80 до 0xff и версией char
.
Почему бы просто не использовать библиотечную процедуру wcstombs
.
Вы ищете wctomb()
: он по стандарту ANSI, поэтому вы можете рассчитывать на него. Он работает, даже если wchar_t
использует код выше 255. Вы почти наверняка не хотите его использовать.
wchar_t
является интегральным типом, поэтому ваш компилятор не будет жаловаться, если вы действительно выполните:
char x = (char)wc;
но поскольку он является интегральным типом, нет абсолютно никаких оснований для этого. Если вы случайно прочитали Herbert Schildt C: The Complete Reference или любую книгу C на основе этого, тогда вы полностью и грубо дезинформировали. Символы должны быть типа int
или лучше. Это означает, что вы должны написать это:
int x = getchar();
а не это:
char x = getchar(); /* <- WRONG! */
Что касается интегральных типов, то char
бесполезно. Вы не должны создавать функции, которые принимают параметры типа char
, и вы не должны создавать временные переменные типа char
, а также те же рекомендации для wchar_t
.
char*
может быть удобным typedef для символьной строки, но это ошибка начинающего, чтобы думать об этом как о "массиве символов" или "указателе на массив символов" - несмотря на то, что cdecl говорит инструмент. Рассмотрение его как фактического массива символов с такими же нонсенсами:
for(int i = 0; s[i]; ++i) {
wchar_t wc = s[i];
char c = doit(wc);
out[i] = c;
}
абсурдно неправильно. Он не будет делать то, что вы хотите; он будет ломаться тонким и серьезным образом, вести себя по-разному на разных платформах, и вы наверняка сбиваете с толку своих пользователей. Если вы видите это, вы пытаетесь переопределить wctombs()
, который уже является частью ANSI C уже, , но он все еще ошибочен.
Вы действительно ищете iconv()
, который преобразует строку символов из одной кодировки (даже если она упакована в wchar_t
array), в строку символов другой кодировки.
Теперь перейдите к this, чтобы узнать, что неправильно с iconv.
Короткая функция, которую я написал некоторое время назад, чтобы упаковать массив wchar_t в массив char. Символы, которые не находятся на кодовой странице ANSI (0-127), заменяются символом '?' символов, и он правильно обрабатывает суррогатные пары.
size_t to_narrow(const wchar_t * src, char * dest, size_t dest_len){
size_t i;
wchar_t code;
i = 0;
while (src[i] != '\0' && i < (dest_len - 1)){
code = src[i];
if (code < 128)
dest[i] = char(code);
else{
dest[i] = '?';
if (code >= 0xD800 && code <= 0xD8FF)
// lead surrogate, skip the next code unit, which is the trail
i++;
}
i++;
}
dest[i] = '\0';
return i - 1;
}
Технически "char
" может иметь тот же диапазон, что и "signed char
" или "unsigned char
". Для символов без знака ваш диапазон правильный; теоретически, для подписанных символов, ваше состояние неверно. На практике очень немногие компиляторы будут возражать - и результат будет таким же.
Nitpick: последний &&
в assert
является синтаксической ошибкой.
Является ли это утверждение подходящим, зависит от того, можете ли вы позволить себе сбой при получении кода клиенту, и что вы могли бы или должны делать, если условие утверждения нарушено, но утверждение не скомпилировано в код. Для работы отладки это кажется прекрасным, но вам может понадобиться активный тест после него для проверки времени выполнения.
можно также преобразовать wchar_t → wstring → string → char
wchar_t wide;
wstring wstrValue;
wstrValue[0] = wide
string strValue;
strValue.assign(wstrValue.begin(), wstrValue.end()); // convert wstring to string
char char_value = strValue[0];
В общем, нет. int(wchar_t(255)) == int(char(255))
, конечно, но это означает, что они имеют одинаковую стоимость int. Они не могут представлять одни и те же символы.
Вы даже увидите такое несоответствие на большинстве ПК с ОС Windows. Например, на стр. 1250 кода Windows char(0xFF)
имеет тот же символ, что и wchar_t(0x02D9)
(точка выше), а не wchar_t(0x00FF)
(малая y с диарезисом).
Обратите внимание, что он даже не поддерживается для диапазона ASCII, так как С++ даже не требует ASCII. В системах IBM, в частности, вы можете увидеть, что 'A' != 65
Вот еще один способ сделать это, не забудьте использовать free() для результата.
char* wchar_to_char(const wchar_t* pwchar)
{
// get the number of characters in the string.
int currentCharIndex = 0;
char currentChar = pwchar[currentCharIndex];
while (currentChar != '\0')
{
currentCharIndex++;
currentChar = pwchar[currentCharIndex];
}
const int charCount = currentCharIndex + 1;
// allocate a new block of memory size char (1 byte) instead of wide char (2 bytes)
char* filePathC = (char*)malloc(sizeof(char) * charCount);
for (int i = 0; i < charCount; i++)
{
// convert to char (1 byte)
char character = pwchar[i];
*filePathC = character;
filePathC += sizeof(char);
}
filePathC += '\0';
filePathC -= (sizeof(char) * charCount);
return filePathC;
}