Код взят отсюда: ссылка
Есть такое решение вышеупомянутой задачи (UTF - 16BE)
:
try (InputStream reader = new FileInputStream("test.txt")) {
long[] unicodeArray = new long[65536];
byte[] buf = new byte[4096];
int len;
short maxChar;
long maxCharCnt = 0;
while ((len = reader.read(buf)) > 0) {
for (int i = 0; i < len; i += 2) {
short c = (buf[i] << 8) || (buf[i + 1] & 0xff);
unicodeArray[c]++;
if (unicodeArray[c] > maxCharCnt) {
maxChar = c;
maxCharCnt = unicodeArray[c];
}
}
System.out.println((char)maxChar);
}
Так как изучаю Java
недавно, сложно даётся нижняя строка и намерения автора, но хочу разобраться.
short c = (buf[i] << 8) || (buf[i + 1] & 0xff);
Я понял так:
Здесь buf[i] << 8
происходит сдвиг влево на байт, получается что младший байт состоит из нулей. Результат кастуется в int, получается подобное:
00000000 00000000 11101110 00000000
Здесь buf[i + 1] & 0xff
"обрезаются" все байты кроме младшего(т.к 0xff == 11111111). Я не понял зачем это нужно, ведь изначально у нас и так был всего один байт. Можно было просто написать (int)buf[i+1]
(?). Результат снова int и выйдет:
00000000 00000000 00000000 10101010
Биты от балды написал.
В итоге имеются два таких int
. Далее применяется побитовый OR(?)
:
00000000 00000000 11101110 00000000
|
00000000 00000000 00000000 10101010
00000000 00000000 11101110 10101010
Результат: 11101110 10101010
, то есть то что написано выше без двух старших байтов?.
Я всё правильно понял?
Помимо обозначенных вопросов:
-Почему по факту это не компилируется? Ошибка: can't apply || with (int)(int)
-Разве так быстрее (на больших объёмах данных) чем считать из файла строку, сразу разбить её на массив char
и работать сразу с символами?
Заранее спасибо.
Я не понял зачем это нужно, ведь изначально у нас и так был всего один байт. Можно было просто написать (int)buf[i+1](?)
.
Приведение производится с сохранением знака, т.е., например, байт 10101010
будет приведен к инту 11111111 11111111 11111111 10101010
.
В остальном все верно, кроме оператора ||
вместо |
в приведенном коде - поэтому и не компилируется.
И да, побитовые операции сами по себе "быстрые", к тому же строки так устроены, что любое изменение происходит через создание новой строки(и копирование массива символов), при переводе строки в массив символов будет возвращена копия всего внутреннего массива, а не указатель на существующий массив.
Для кодирования UTF - 16BE используются 16 бит*, если не отводить один бит на знак, то 16 битами можно представить 65536 (2^16) значения, такое "беззнаковое" использование характерно для примитивного типа char в Java.
Некоторые пояснения
Используем массив
long[] unicodeArray = new long[65536];
для хранения количества вхождений каждого из встретившихся символов. Цикл
while((len = reader.read(buf)) > 0) {}
последовательно считывает байты в массив byte[] buf, когда читать нечего, read возвращает -1 и цикл прерывается. Для обработки считанных в массив buf данных, используется цикл
for (int i = 0; i < len; i += 2) {}
Выражение
char c = (char)((buf[i] << 8) | (buf[i + 1] & 0xff));
формирует 16 битное представления символа(16 бит = 2 байта) из считанных в данных. Буфер buf это массива байт, т.е нам нужно брать по два байта из массива и "склеивать" их, для. "Логика склеивания" следующая.
Предположим что: buf[i] содержит последовательность бит (это один байт из файла)
10000001 (-127)
При использовании побитовых операторов происходит неявное преобразование типов к int. При преобразовании в int получим
11111111 11111111 11111111 10000001
Выражение
(buf[i] << 8)
выполняет сдвиг влево на 8 бит, получаем
11111111 11111111 10000001 00000000
Мы получили первый байт, смещенный на 8 бит влево, т.е. освободили место для второго байта
Далее предположим buf[i+1] содержит значение последовательность бит
10000000 (-128)
при преобразовании в int получим (расширение учитывает знак)
11111111 11111111 11111111 10000000
Выражение
(buf[i + 1] & 0xff)
побитно перемножает
11111111 11111111 11111111 10000000 - получено из buf[i+1]
&
00000000 00000000 00000000 11111111 - это 0xff в двоичной форме
=
00000000 00000000 00000000 10000000
В результате второй байт расположен в последних 8 битах
Далее
(buf[i] << 8) | (buf[i + 1] & 0xff)
складывает преобразованный и смещенный первый байт (из buf[i]) и подготовленный второй (из buf[i+1])
11111111 11111111 10000001 00000000
|
00000000 00000000 00000000 10000000
=
11111111 11111111 10000001 10000000
Полученное значение имеет размерность int, оно явно преобразуется в char(отсекаются первые 16 бит, если считать слева направо) и сохраняется в переменной char с . В результате получаем то, что хотели ("склееные" два байта)
10000001 10000000
Полученное значение используется как "адрес символа" в массиве, и производится инкремент "счетчика" для данного символа
unicodeArray[c]++;
Вот такой код работает.
import java.io.FileInputStream ;
import java.io.InputStream ;
import java.io.IOException ;
public class MaxFinder {
public static void main(String[] args) throws IOException {
try (InputStream reader = new FileInputStream("test.txt")) {
//массив примитивных типов long
//для хранения количества вхождений каждого из встретившихся символов
long[] unicodeArray = new long[65536];
//массив для хранения считанных данных
byte[] buf = new byte[4096];
//сколько байт прочитали в буфер
int len;
//самый часто встречающийся символ
char maxChar = 0;
//количество вхождений самого часто встречающегося символа
long maxCharCnt = 0;
//в цикле читаем данные в буфер, пока не закончатся данные (read вернет -1)
while ((len = reader.read(buf)) > 0) {
//в цикле обрабабатываем по 2 байта из буфера,
//из которых формируют один символ в UTF - 16
for (int i = 0; i < len; i += 2) {
//формирум 16 битное представления из считанных байт
//при использовании побитовых операторов
//(buf[i] << 8) - сдвиг первый байт на 8 бит влево
//"освобождает" место для второго байта
//(buf[i + 1] & 0xff) "готовит" второй байт
//унарные операторы преобразуют тип к int
//преобразуем в тип char (отсекая первые 16 бит слева)
char c = (char)((buf[i] << 8) | (buf[i + 1] & 0xff));
//используем char (диапазон значений от 0 до 65536) как адрес в массиве,
//и увеличивая счетчик для соответствующего символа
unicodeArray[c]++;
//если больше текущего максимума, обновляем максимум
if (unicodeArray[c] > maxCharCnt) {
maxChar = c;
maxCharCnt = unicodeArray[c];
}
}
}
System.out.println((char)maxChar);
}
}
}
Виртуальный выделенный сервер (VDS) становится отличным выбором
Всем привет, знаю, что уже очень много вопросов на эту тему, однако, как грамотно использовать Android storage access frameworkВот примерно такой:
Ребята нужна помощь и это касается быстрого поискаПишу загрузку данных из DBF в Postgresql
Пытаюсь расширить адаптер для спиннера, написал класс включающий в себя проверку: находится ли в списке данных для спиннера нужная строка: