Как создать строку из массива байтов типа int?
Например, имеется массив из N элементов, в котором индексы i и i+1 образуют по таблице UTF-8 русский символ. Как их объединить и на основе этого создать строку?
Пример:
array[0] = 208;
array[1] = 144;
По таблице UTF-8 dec: А - 208 144
Такое получилось после того, как из строки считал байты и перевел их в массив int.
CODE:
String sKey = LFSR();
System.out.println("[PSP]: key: " + sKey); // output: 10011010
byte[] txt = text.getBytes(StandardCharsets.UTF_8);
byte[] key = sKey.getBytes(StandardCharsets.UTF_8);
byte[] res = new byte[txt.length];
int[] bytes = new int[txt.length];
// Шифруем текст
for (int i = 0; i < txt.length; ++i) {
res[i] = (byte) (txt[i] ^ key[i % key.length]);
}
// здесь выводит иероглифы из-за того, что байты отрицательные и они съедаются.
System.out.println("Зашифрованный текст:" + new String(res, StandardCharsets.UTF_8));
// Расшифровка текста
for (int i = 0; i < txt.length; ++i) {
res[i] = (byte) (res[i] ^ key[i % key.length]);
bytes[i] = res[i] & 0xff; // восстанавливаю байты, избавляюсь от отрицательных
}
for (int i = 0; i < bytes.length; i++) {
System.out.print(bytes[i] + " ");
}
// первоначальный текст
System.out.println("Расшифрованный текст:" + new String(res, StandardCharsets.UTF_8));
Ответы (3 шт):
Решение вопроса перекодировки: (UTF-8 decimal -> Unicode)
class UnicodeUtils {
static int[] convertUtf8DecimalToUnicode(int[] utf8DecimalCodePoints) {
List<Integer> result = new ArrayList<>();
int byteShift = 0;
int accumulatingUnicodeIndex = 0;
for (int utf8DecimalCodePoint : utf8DecimalCodePoints) {
switch (utf8DecimalCodePoint & 0b1111_0000) {
case 0b1000_0000:
case 0b1001_0000:
case 0b1010_0000:
case 0b1011_0000:
--byteShift;
int significantBits = (utf8DecimalCodePoint & 0b11_1111) << (byteShift * 6);
accumulatingUnicodeIndex = accumulatingUnicodeIndex | significantBits;
break;
case 0b1100_0000:
case 0b1101_0000:
byteShift = 1;
accumulatingUnicodeIndex = getSignificantBits(utf8DecimalCodePoint, byteShift);
break;
case 0b1110_0000:
byteShift = 2;
accumulatingUnicodeIndex = getSignificantBits(utf8DecimalCodePoint, byteShift);
break;
case 0b1111_0000:
byteShift = 3;
accumulatingUnicodeIndex = getSignificantBits(utf8DecimalCodePoint, byteShift);
break;
default:
accumulatingUnicodeIndex = utf8DecimalCodePoint;
break;
}
if (byteShift == 0) {
result.add(accumulatingUnicodeIndex);
accumulatingUnicodeIndex = 0;
}
}
return result.stream().mapToInt(i -> i).toArray();
}
private static int getSignificantBits(int utf8DecimalCodePoint, int utf8DecimalByteNumber) {
int utf8ServiceBits = utf8DecimalByteNumber + 2;
int significantBitsMask = 0b1111_1111 >>> utf8ServiceBits;
int significantBitsShifts = utf8DecimalByteNumber * 6;
return (utf8DecimalCodePoint & significantBitsMask) << (significantBitsShifts);
}
static String conventUnicodeToString(int[] unicodeIndexes) {
StringBuilder stringBuilder = new StringBuilder(unicodeIndexes.length);
for (int i : unicodeIndexes) {
stringBuilder.append(Character.toChars(i));
}
return stringBuilder.toString();
}
}
public class Example {
public static void main(String[] args) {
int[] utf8Decimal = {48, 57, 97, 122, 65, 90, 208, 176, 209, 143, 208, 144, 208, 175, 209, 145, 208, 129}; // 09azAZаяАЯёЁ
int[] unicodeIndexes = UnicodeUtils.convertUtf8DecimalToUnicode(utf8Decimal);
System.out.println(UnicodeUtils.conventUnicodeToString(unicodeIndexes));
}
}
Решение вопроса с перекодировкой: (со сдвигом на 20, по результатам общения в комментариях)
class EncodingUtils {
static byte[] encode(byte[] originalBytes) {
byte[] encodedBytes = new byte[originalBytes.length];
for (int i = 0; i < originalBytes.length; ++i) {
encodedBytes[i] = (byte) (originalBytes[i] + 20);
}
return encodedBytes;
}
static byte[] decode(byte[] encodedBytes) {
byte[] decodedBytes = new byte[encodedBytes.length];
for (int i = 0; i < encodedBytes.length; ++i) {
decodedBytes[i] = (byte) (encodedBytes[i] - 20);
}
return decodedBytes;
}
}
public class Example {
public static void main(String[] args) {
byte[] originalBytes = {-128, -10, 0, 10, 127};
byte[] encodedBytes = EncodingUtils.encode(originalBytes);
byte[] decodedBytes = EncodingUtils.decode(encodedBytes);
}
}
Символ А имеет значение 1040 -> 0000010000010000 (2 байта):
00000100-- значение 400010000-- значение 16
Небольшой тест:
byte[] bytes = {(byte) 4, (byte) 16};
System.out.println(ByteBuffer.wrap(bytes).getChar());
// А
По такому же принципу получаем слово:
int[] array = {4, 31, 4, 64, 4, 56, 4, 50, 4, 53, 4, 66};
StringBuilder builder = new StringBuilder();
for (int i = 0; i < array.length; i += 2) {
byte[] bytes = {(byte) array[i], (byte) array[i+1]};
builder.append(
ByteBuffer.wrap(bytes).getChar()
);
}
System.out.println(builder);
// Привет
Рабочая функция перекодировки (на Си), возвращает количество успешно перекодированных UCS, при ошибке по указателю utf_err пишет индекс + 1 первого ошибочного входного элемента последовательности UTF-8 (если ошибок нет, пишет туда 0), может кому-то окажется полезной
(по крайней мере алгоритм)
#ifndef T
#define T char
#endif
size_t
convert_utf8_to_ucs (T utf8[], uint32_t ucs[], size_t ucs_size,
size_t *utf_err)
{
size_t n = 0,
i = 0,
dummy;
if (!utf_err)
utf_err = &dummy;
*utf_err = 0;
while (utf8[i]) {
if (n < ucs_size) {
uint32_t uc = utf8[i++] & 0xff; // utf-8 is byte coded
if (uc < 128)
ucs[n++] = uc; // ascii
else {
if (uc < 0xC1 || uc > 0xF7) {
// RFC 3629 permits only up to 4 bytes coding
*utf_err = i;
break;
}
size_t nb = 1; // 1 aux byte for the begin;
for (int i = 5; uc & (1 << i); nb++, i--);
uc &= ((1 << (7 - nb)) - 1); // high bits of UCS
while (nb--) {
uint32_t t = utf8[i++];
if ((t & 0xC0) != 0x80) {
*utf_err = i;
return n; // error utf-8 coding
}
uc = ((uc << 6) | (t & 0x3F));
}
ucs[n++] = uc;
}
} else {
*utf_err = i + 1;
break;
}
}
return n;
}