Заменить все строчные буквы на заглавные и наоборот, но только на кириллице Си
С латиницей всё довольно просто:
#include <stdio.h>
int main()
{
char s[] = "Hello, WoRlldoQQ!!";
for (int i = 0; s[i] != '\0'; i++) {
if (s[i] >= 'a' && s[i] <= 'z')
s[i] -= 'a' - 'A';
else if (s[i] >= 'A' && s[i] <= 'Z')
s[i] += 'a' - 'A';
}
printf("%s\n", s);
return 0;
}
А вот Русские символы кодируются 2 байтами в UTF-8 и я не знаю как это можно реализовать, помогите)
Ответы (2 шт):
В итоге использовал широкие строки
#include <stdio.h>
#include <locale.h>
int main()
{
setlocale(LC_ALL, "Rus");
wchar_t s[] = L"ПриВет МиР; HelLO WORld!!!";
for (int i = 0; s[i] != '\0'; i++) {
wchar_t c = s[i];
if (c >= 'a' && c <= 'z')
s[i] -= 'a' - 'A';
else if (c >= 'A' && c <= 'Z')
s[i] += 'a' - 'A';
else if (c >= L'а' && c <= L'я')
s[i] -= L'а' - L'А';
else if (c >= L'А' && c <= L'Я')
s[i] += L'а' - L'А';
}
printf("%S\n", s);
return 0;
}
Вот пример, как можно сделать оставаясь в UTF-8
(признаюсь, парой сравнений дело не ограничилось, но как и обещал в комментариях, совершенно без перекодировочных таблиц в памяти и без преобразований в unicode (т.е. wide chars)).
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <ctype.h>
// 5 high bits of first utf-8 byte is key to the size of utf-8 symbol
// 1 1 1 1 1 1 1 1
// 1 1 1 1 1 1 1 1 // 16 ascii
// 1 1 1 1 1 1 1 1 // 8 cont
// 2 2 2 2 3 3 4 1 // 1 -- err
#define UTF8_LTAG (0x3a55ULL << 48)
#define UTF8_LEN(C) ({ \
int _x = ((C) >> 3) & 0x1f; \
int _l = (UTF8_LTAG >> (_x << 1)) & 3; \
_l + 1; })
void
change_utf8_rus_case (unsigned char *s)
{
if (s[0] == 0xd0) {
if ((s[1] > 0x8f && s[1] < 0xa0) || s[1] > 0xaf)
s[1] ^= 0x20;
else if ((s[1] > 0x9f && s[1] < 0xb0))
s[0] ^= 1, s[1] ^= 0x20;
else if (s[1] == 0x81) // Ё
s[0] = 0xd1, s[1] = 0x91;
} else if (s[0] == 0xd1) {
if (s[1] >= 0x80 && s[1] < 0x90)
s[0] ^= 1, s[1] ^= 0x20;
else if (s[1] == 0x91)
s[0] = 0xd0, s[1] = 0x81; // Ё
}
}
int
main (int ac, char *av[])
{
char str[1024] = "ПриВет МиР; ёЁ; HelLO WORld!!!";
puts(str);
int step;
for (int i = 0; str[i]; i += step) {
step = UTF8_LEN(str[i]);
if (step == 1 && isalpha(str[i]))
str[i] ^= 0x20;
else if (step == 2)
change_utf8_rus_case((unsigned char *)(str + i));
}
puts(str);
return puts("End") == EOF;
}
Компилируем и запускаем
avp@avp-desktop:~/avp/hashcode$ g++ -O2 utfrus.cpp && ./a.out
ПриВет МиР; ёЁ; HelLO WORld!!!
пРИвЕТ мИр; Ёё; hELlo worLD!!!
End
avp@avp-desktop:~/avp/hashcode$
Еще одна реализация функции для изменения регистра русских букв использует перевод символа из кодировки utf-8 в 32-бит Unicode, но так же не использует никаких структур в памяти
(обе реализации функций смены регистра можно вызывать только для 2-х байтных символов utf-8).
void
chng_case_rus_utf (unsigned char *s)
{
uint32_t v = (s[0] & 0x1f) << 6;
v |= (s[1] & 0x3f);
if (v > 0x40f && v < 0x450) {
if (v < 0x430)
v += 32;
else
v -= 32;
s[0] = 0xc0 | ((v >> 6) & 0x1f);
s[1] = 0x80 | (v & 0x3f);
} else if (v == 0x401) // Ё
s[0] = 0xd1, s[1] = 0x91;
else if (v == 0x451) // ё
s[0] = 0xd0, s[1] = 0x81;
}
Обе реализации имеют примерно одинаковый размер на ассемблере (от 80 до 128 байт, первенство зависит от оптимизации (с -Os chng_case_rus_utf немного больше, а с -O2 чуть меньше)) и выполняются за примерно одинаковое время (chng_case_rus_utf чуть медленнее).
Что не понятно, спрашивайте.