Модераторы: LSD, AntonSaburov
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Array.equals - понять нюансы, Не вполне по "Doc"-ам? 
:(
    Опции темы
Gantenbain
Дата 20.1.2008, 10:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 47
Регистрация: 16.10.2007

Репутация: нет
Всего: нет



Всем доброго дня!

Опять наступаю на эти грабли, возможно, кто-нибудь рассеит мрак моего невежества в сем вопросе на этот раз:
 
Код

      RandomAccessFile ra = new RandomAccessFile(f, "rw");
          FileChannel channel = ra.getChannel();

          BufferedReader breader = new BufferedReader(new FileReader(new File(tmpPath + "deafut.txt")));
          String line;
          while((line = breader.readLine()) != null) {
             String str = line.substring(0, line.indexOf(",")).trim();
             byte[] bstr = str.getBytes();
             ByteBuffer small = ByteBuffer.wrap(bstr);

System.out.println((Charset.forName("Cp1251").decode(ByteBuffer.wrap(bstr))).toString());
             
             for(int i = 0; i < channel.size(); i++) {        // find into channel
                 small.clear();
                 channel.position(i);
                 channel.read(small);
                 byte[] ref = small.array();//new byte[bstr.length];

                 if(Arrays.equals(bstr, ref)) { // ВОТ ОНО КАКОЕ...!
System.out.println((Charset.forName("Cp1251").decode(ByteBuffer.wrap(ref))).toString());
                   channel.position(i);
                   reach.clear();
                   channel.read(reach);
                   reach.flip();
                   byte[] allstr = (line + '\n').getBytes();
                   ByteBuffer allStrByte = ByteBuffer.wrap(allstr);
                   allStrByte.rewind();
                   channel.position(i);
                   channel.write(allStrByte);
                   channel.write(reach);
                   break;
                 }
             }
          }
          breader.close();
          channel.force(false);
          channel.close();
          ra.close();
          reach.clear();

     } catch (IOException e) {


В середку большого файла засовываются куски файла небольшого (оба - текст UTF-8), место вставки ищу, сравнивая int-массивы. 
Строки, выводимые на консоль, совпадают лишь по длине (?).. Между тем, имею практически идентичную работающую схему в другой программе, где правда контент чисто цифровой...
Чего не вижу, где туплю?
PM MAIL WWW   Вверх
Gantenbain
Дата 20.1.2008, 14:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 47
Регистрация: 16.10.2007

Репутация: нет
Всего: нет



Проблема лежит где-то в области декодирования, т.к. если сравнивать строки, полученные из этих byte-массивов (напр: ((Charset.forName("UTF-8").decode(ByteBuffer.wrap(ref))).toString());), то все работает, но. разумеется, жутко медленно. 

 smile т.е вопрос-то, кажется, вовсе не к корректности метода equals...

Это сообщение отредактировал(а) Gantenbain - 20.1.2008, 14:54
PM MAIL WWW   Вверх
Sardar
Дата 20.1.2008, 15:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бегун
****


Профиль
Группа: Модератор
Сообщений: 6986
Регистрация: 19.4.2002
Где: Нидерланды, Groni ngen

Репутация: 4
Всего: 317



Код не корректный, getBytes() возвращает не UTF-8, а строку в локально (системной) кодировке, которая различается на разных системах (и даже у отдельных пользователей).
Вывод: вместо Array.equals() нужно использовать equals() строки.

Цитата(Gantenbain @  20.1.2008,  13:27 Найти цитируемый пост)
но. разумеется, жутко медленно

Это чисто предположение или есть результаты тестов? Перекодирование - операция почти мгновенная, по сравнению с операциями ввода-вывода.


--------------------
 Опыт - сын ошибок трудных  © А. С. Пушкин
 Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik
 Оценить мои качества можно тут.
PM   Вверх
Gantenbain
Дата 20.1.2008, 16:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 47
Регистрация: 16.10.2007

Репутация: нет
Всего: нет



Цитата(Sardar @  20.1.2008,  15:48 Найти цитируемый пост)
Код не корректный, getBytes() возвращает не UTF-8, а строку 
 А раньше байтовый массив возвращал! Что делается....
Цитата(Sardar @  20.1.2008,  15:48 Найти цитируемый пост)
Вывод: вместо Array.equals() нужно использовать equals() строки.

Увы, с таким выводом не соглашусь - желательно понять механизм перекодировки, т.к. от операций со строками, стараюсь всегда уйти, в особенности, если работа происходит с большими документами или связана с большим количеством операций (хотя, кому как,  для некоторых 2Гб-ые массивы и сотня перестановок в них - мелочь). Строка - объект, требует свою область памяти -   проблема-то не в скорости самого "перекодирования". 
Цитата(Sardar @  20.1.2008,  15:48 Найти цитируемый пост)
результаты тестов
 имелись, когда задавался этим вопросом.
Что касается UTF-8: файл читаю
Код

BufferedReader breader = new BufferedReader(new InputStreamReader(
                                     new FileInputStream(tmpPath + "deafut.txt"), "UTF-8"));

 Попытка сравнивать CharBuffer-ы путем подобных конструкций:
Код

                 channel.read(small, channel.position());
                 byte[] ref = new byte[bstr.length];
                 System.arraycopy(small.array(), 0, ref, 0, small.array().length);
                 CharBuffer chref = Charset.forName("UTF-8").decode(ByteBuffer.wrap(ref));

опять же Arrays.equals, ни к чему хорошему опять же не приводит.... Т.е. непонимание мое глубоко  smile 
PM MAIL WWW   Вверх
Sardar
Дата 20.1.2008, 17:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бегун
****


Профиль
Группа: Модератор
Сообщений: 6986
Регистрация: 19.4.2002
Где: Нидерланды, Groni ngen

Репутация: 4
Всего: 317



Цитата(Gantenbain @  20.1.2008,  15:25 Найти цитируемый пост)
 А раньше байтовый массив возвращал! Что делается....

Под строкой имел в виду 8-битную строку (a.k.a байтовый массив), не стоит придираться к словам ;-)

Цитата(Gantenbain @  20.1.2008,  15:25 Найти цитируемый пост)
желательно понять механизм перекодировки, т.к. от операций со строками, стараюсь всегда уйти, в особенности, если работа происходит с большими документами или связана с большим количеством операций

Всё, что тебе нужно - это сравнить две строки (сужу по коду выше). JIT оптимизатор может представить твой байтовый массив массивом int'ов (для небольших размеров это быстрей), а памяти займёт в 4 раза больше чем ты думаешь. Сам проц с байтами никогда не работает, отсюда проблемы с выравниванием, поэтому случайный доступ в байтовом массиве вещь дорогая (потому часто в int'ах и представляется). Благо в твоём коде это не нужно.

Строка - массив char'ов. Используются простейшие табличные перекодировки и collating rules, работающие близко к O(1) (по хешу в range входим). Сложности байтовых массивов тут тоже действуют конечно. Большой плюс в корректности сравнений, учитывается семантика символа натурального (человеческого) языка, а не тупо сравнение по цифровому коду (актуально для китайского и подобных). Ты ведь знаешь что уникодовский символ занимает 4 байта ;-)

Цитата(Gantenbain @  20.1.2008,  15:25 Найти цитируемый пост)
Попытка сравнивать CharBuffer-ы путем подобных конструкций:

Если default.txt не был записан в UTF-8, то читаешь ты лажу (сработает только для чистого ASCII текста). Затем через getBytes() ты перегоняешь текст в системную кодировку, т.е. на моём компе кириллица в юникоде станет '?' в CP1251.
Отсюда слова:
Цитата(Gantenbain @  20.1.2008,  09:02 Найти цитируемый пост)
В середку большого файла засовываются куски файла небольшого (оба - текст UTF-8)

теряют смысл. UTF-8 кодирует символ до 6 байт (могу ошибаться, до 8), т.е. так просто "вставить" текст посередине UTF-8 потока символов нельзя. Если это не ясно, то стоит хорошо изучить понятие кодировок, а также отойти от 8-битных строк.

Вывод: по прежнему отказаться от 8-битных строк и пользоваться String, хорошо оптимизируемых (ибо не модифицируемы в принципе) VM.
На раздумья: написать тесты на эффективность строковых операций и перекодировок, т.к. по своему опыту замечу:
Цитата(Gantenbain @  20.1.2008,  15:25 Найти цитируемый пост)
имелись, когда задавался этим вопросом.

как минимум устарело, либо высосано из пальца (прошу прощение за выпад).



--------------------
 Опыт - сын ошибок трудных  © А. С. Пушкин
 Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik
 Оценить мои качества можно тут.
PM   Вверх
LSD
Дата 20.1.2008, 20:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Leprechaun Software Developer
****


Профиль
Группа: Модератор
Сообщений: 15718
Регистрация: 24.3.2004
Где: Dublin

Репутация: 210
Всего: 538



Как минимум надо исправить следующие ошибки:
Код

channel.read(small);
byte[] ref = small.array();

если почитать JavaDoc по методу read(), то станет ясно, что он может прочитать произвольное количество байт, а не обязательно заполнить весь буфер. Именно для этого он и возвращяет число прочитанных байт.

Код

byte[] bstr = str.getBytes();

в данном случае требуется явно указать кодировку.

Ну и хотелось бы видеть код который можно запустить.


--------------------
Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it.
PM MAIL WWW   Вверх
Gantenbain
Дата 20.1.2008, 20:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 47
Регистрация: 16.10.2007

Репутация: нет
Всего: нет



Уважаемый, Sardar,
я очень тронут обилием текста в Вашем посте, полагая время, потраченное Вами, мерой внимания к моей проблеме,  но огорчен отсутствием постяжимого мне смысла, хотя, как человек вежливый, старательно искал в Вашем спиче повод поблагодарить.  Но, как не постичь неискушенному в массонских собраниях "семантику символа", так и мне, увы, не открылось, хотели Вы мне указать какой-либо путь, либо Вам вдруг вспомнились размерности shar и int и Вы сочли мой вопрос подходящим поводом разрядить свою память "во вне". Сделайте над собой еще небольшое усилие, и я в своем профайле обязательно занесу Вас в "друзья".
Цитата(Sardar @  20.1.2008,  17:04 Найти цитируемый пост)
Всё, что тебе нужно - это сравнить две строки 

Заметьте, одна из этих "строк" каждый раз новая и отличается от предыдущей лишь на один! символ, являясь между тем полноценным объектом. Вы полагаете, что при каждой новой иттерации ссылочка на этот объект будет разрушена, и вероятно, желаете предложить мне "на раздумья" в этом убедиться? Я, видимо,  целиком 
Цитата(Sardar @  20.1.2008,  17:04 Найти цитируемый пост)
как минимум устарел
 Но дискуссия о том, пользоваться ли мне строками или чем иным, в мои планы не входила.
Цитата(Sardar @  20.1.2008,  17:04 Найти цитируемый пост)
а памяти займёт в 4 раза больше чем ты думаешь
 Вот это мне как раз все равно,  равно, как все равно, в какую кодировку перекодируется файл, важно, чтобы ОБА файла, в момент чтения их мною, были в одной кодировке (точнее, я предполагаю, что это важно ;-) ).  Но Ваши умозаключения "вблизи" кодировок, не подсказывают мне ни в малейшей мере, что мне надобно сделать, дабы уверенно организовать именно такое чтение...
Еще раз обращу Ваше драгоценное внимание (Вы ведь не лишите меня его?): 
от особенностей чтения в локальных установках getBytes(), пытался избавиться указывая для чтения файлу, от которого беру строку для сравнения, FileInputStream с кодировкой UTF8, поскольку полагаю, что BufferedReader и FileInputStream вернее свернут голову файлу, нежели getBytes(). Но любые комбинации для байтового массива, из которого формирую строку для сравнения с эталоном, - напр.
Charset.forName("UTF-8").decode(ByteBuffer.wrap(ref)) и т.п., - не дают мне возможность воспользоваться методом Arrays.equals(). Вот попытайтесь, как человек "хорошо изучивший понятие кодировок", объяснить мне, какие преобразования нужны, чтобы привести файли в один читабельный стандарт. Если я имею два идентичных куска файла, то какие бы я не проделал операции над ними, если над каждым будут произведены те же операции,что и над другим, идентичность фрагментов не потеряется (или у Вас есть нечто противное из "своего опыта"?).  А замечания типа 
Цитата(Sardar @  20.1.2008,  17:04 Найти цитируемый пост)

т.е. так просто "вставить" текст посередине UTF-8 потока символов нельзя
 Это, уважаемый, всего лишь еще один способ сказать "не знаю".
Любопытства ради - в файлах только текст и латынь.

PM MAIL WWW   Вверх
Gantenbain
Дата 20.1.2008, 20:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 47
Регистрация: 16.10.2007

Репутация: нет
Всего: нет



to LSD
в данном случае, писать цикл с read() не стал, т.к. конструкция давно не давала сбоев и не самое ответственное место, честно говоря, хотя подумаю, добавить три строчки кода не проблема.

Насчет "запустить код", признаться честно, нет времени совершенно для составления показательного примера, а рабочий код сопровождается большой "портянкой" файла (но тоже можно подумать..).
А пока работает вот так:
 for(int i = 0; i < channel.size(); i++) {        // begin find into channel
                 small.clear();
                 channel.position(i);
                 channel.read(small, channel.position());
                 byte[] ref = new byte[bstr.length];
                 System.arraycopy(small.array(), 0, ref, 0, small.array().length);
                 CharBuffer chref = Charset.forName("UTF-8").decode(ByteBuffer.wrap(ref));
                 if(str.equals(chref.toString())) {
                   channel.position(i);
                   reach.clear();
                   channel.read(reach, channel.position());
                   reach.flip();
                   byte[] allstr = (line + '\n').getBytes();
                   ByteBuffer allStrByte = ByteBuffer.wrap(allstr);
                   allStrByte.rewind();
                   channel.position(i);
                   channel.write(allStrByte);
                   channel.write(reach);
                   isFound = true;
                   break;
                 }
"На скору руку", но все-таки полагаю это паллиативом
P.S. Глянул в "Доки":
Returns:
The number of bytes read, possibly zero, or -1 if the channel has reached end-of-stream 
Не вижу проблемы в своем случае.

Это сообщение отредактировал(а) Gantenbain - 20.1.2008, 21:16
PM MAIL WWW   Вверх
Sardar
Дата 20.1.2008, 21:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бегун
****


Профиль
Группа: Модератор
Сообщений: 6986
Регистрация: 19.4.2002
Где: Нидерланды, Groni ngen

Репутация: 4
Всего: 317



Ё... похоже тебе просто хочется потрепаться (извини за выпад  smile )...

Цитата(Gantenbain @  20.1.2008,  19:38 Найти цитируемый пост)
Заметьте, одна из этих "строк" каждый раз новая и отличается от предыдущей лишь на один! символ,

Пока в файле ASCII, двигаться по position(i) можно, но как только там появиться кирилица, то сдвиг на "символ" выполнить будет сложней (если в файле UTF-8 конечно, исхожу из первого поста). Сдвиг на часть символа не допустимо, т.к. искомая строка (при невезении) может совпасть и можно написать в середину символа.

Лечиться это так:
  • считываем символы с потока. Люди обычно используют не буфферизованый InputStreamReader, для тебя же будет полезно (в целях развития) считать в ручную (посчитать биты в префиксе первого байта, элементарно).
  • в любом символы складываются в char буффер, лучше всего подходит CharBuffer, т.к. он сразу CharSequence, без излишнего копирования.
  • используем equals() искомой строки для сравнения
  • заполняем буффер дальше одним символом
Хорошо что в последнем варианте до тебя это наконец дошло, хотя считывание всё равно не корректно (сдвиг по байту).

P.S. что то я разозлился  smile . тема себя исчерпала, остальное лечиться книжками/мануалами. сюда более не вернусь, потому вы Gantenbain, можете держать ваш сарказм при себе, другим читать приятней.


--------------------
 Опыт - сын ошибок трудных  © А. С. Пушкин
 Процесс написания своего велосипеда повышает профессиональный уровень программиста. © Opik
 Оценить мои качества можно тут.
PM   Вверх
Gantenbain
Дата 27.1.2008, 16:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 47
Регистрация: 16.10.2007

Репутация: нет
Всего: нет



Ввиду нехватки времени, так и не удается толком разобраться с особенностями чтения файлов различными классами. Но видел топик на форуме - с близким по теме вопросом, поэтому допишу сюда пару наблюдений, может и пригодится кому.
Свой код переписал под Arrays.equal():
Код

       ByteBuffer reach = ByteBuffer.allocateDirect((int)(arrF[0].length() + arrF[1].length()));
          RandomAccessFile ra = new RandomAccessFile(f, "rw");
          FileChannel channel = ra.getChannel();
           
          BufferedReader breader = new BufferedReader(new InputStreamReader(
                                     new FileInputStream(tmpPath + "deafut.txt"), "UTF-8"));
          String line;
          while((line = breader.readLine()) != null) {
             String str = line.substring(0, line.indexOf(",", line.indexOf("\"", 1))).trim();
             byte[] bstr = str.getBytes("UTF-8");
             byte[] bbyte = new byte[bstr.length];
             System.arraycopy(bstr, 0, bbyte, 0, bstr.length);
             ByteBuffer small = ByteBuffer.wrap(bbyte);
             boolean isFound = false;
            
             for(int i = 0; i < channel.size(); i++) {        // begin find into channel
                 small.clear();
                 channel.position(i);
                 channel.read(small, channel.position());
                 byte[] ref = new byte[bstr.length];
                 System.arraycopy(small.array(), 0, ref, 0, small.array().length);
                 if(Arrays.equals(bstr, ref)) {
                   channel.position(i);
                   reach.clear();
                   channel.read(reach, channel.position());
                   reach.flip();
                   ByteBuffer allStrByte = ByteBuffer.wrap((line + '\n').getBytes("UTF-8"));
                   allStrByte.rewind();
                   channel.position(i);
                   channel.write(allStrByte);
                   channel.write(reach);
                   isFound = true;
                   break;
                 }
             }
             if(!isFound) {
                 channel.position(channel.size());
                 channel.write(ByteBuffer.wrap((line + '\n').getBytes("UTF-8")));
                }
          }
          breader.close();
          channel.force(false);
          channel.close();
          ra.close();
          reach.clear();
          wrapZip(f);


Если для чтения файлов используются классы Reader, нужно внимательно отслеживать преобразование кодировки источника, используя, при необходимости, потоки, конструкторы которых допускают явное указание таковой, напр.:
Код

BufferedReader breader = new BufferedReader(new InputStreamReader(
                                     new FileInputStream(file), "UTF-8"));

И если сравниваемые файлы будут таки к единой форме приведены, то проблемы сравнивать их как байтовые массивы, нет.
Наблюдения, которым с ходу не нашел в Доках объяснений, а глубоко в потроха лезть некогда:
Код

String str = "All you need is ...";
ByteBuffer small = Charset.forName("UTF-8").encode(str);
byte[] bstr = new byte[small.array().length];

byte[] byte = str.getBytes("UTF-8");

Для меня было неожиданно, что длина byte короче, нежели bstr (?). При этом классы Writer & Reader, и преобразования этих массивов в объект-строку (разумеется, если сначала System.arraycopy(small.array(), 0, bstr, 0, small.array().length);) возвращают строку совершенно "equals", т.е. последовательность кодированных символов (разумеется, преобразуем к одной кодировке) одна и та же. Соответственно, пришлось бросить encode/decode, чтобы не заморачиваться с файловым каналом, и оставить wrap и getBytes с кодировкой.
Быстрее ли работает сравнение массивов.... Прогнал оба варианта на небольших файлах (к концу года они сильно "поправятся", а сейчас не до тестов) - вариант с Arrays.equals несколько быстрее, чем сравнение строк (чуть более 10%), память оба варианта почти не едят, - т.е. выигрыш мало заметен, есть над чем задуматься, но в данной моей задаче, совершенно отказаться от чтения строки и рассматривать единственно байт-код не удастся... 

Это сообщение отредактировал(а) Gantenbain - 27.1.2008, 16:55
PM MAIL WWW   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Java"
LSD   AntonSaburov
powerOn   tux
javastic
  • Прежде, чем задать вопрос, прочтите это!
  • Книги по Java собираются здесь.
  • Документация и ресурсы по Java находятся здесь.
  • Используйте теги [code=java][/code] для подсветки кода. Используйтe чекбокс "транслит", если у Вас нет русских шрифтов.
  • Помечайте свой вопрос как решённый, если на него получен ответ. Ссылка "Пометить как решённый" находится над первым постом.
  • Действия модераторов можно обсудить здесь.
  • FAQ раздела лежит здесь.

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux, javastic.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Java: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0613 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.