java.выбор_коллекции

Вопросы написания собственного программного кода (на любых языках)

Модератор: Olej

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: java.выбор_коллекции

Непрочитанное сообщение Olej » 07 янв 2015, 22:49

Olej писал(а): Легко видеть:
- здесь и 32-бит и 64-бит ...
- и ядра Linux от 2.6.32 до 3.17 ...
- и дистрибутивы от Fedora до Ubuntu ...
А результаты совершенно идентичные и верные.

Осталось разобраться в чём может выражаться разница в работе итератора Scanner.
Всё. Разобрался. ;-)

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

Код: Выделить всё

import java.io.*;
import java.util.*;

class sctest {

   static String [] tests = {
      "AB13 0JW,+57.1,-2.23,+38,+38,",
      "AB13 xxx,+57.1,-2.23,+38,+38,-1,-1,-1,-1,-1,1",
      "AB13 0JU,57.112005,-b.234087,385921,385921,",
      "1.1, 4, 5.6,2,-4,-3.3",
   };

   static void parse( String line ) {
      Scanner scan = new Scanner( line );
      scan.useDelimiter( "," );
      String code = "";
      double x = -111.111, y = 111.111;
      if( scan.hasNext() ) code = scan.next(); 
      if( scan.hasNext() )
         try { x = new Double( scan.next() ).doubleValue(); }
         catch( java.lang.NumberFormatException ex ) { x = -999.999; }
      if( scan.hasNext() ) 
         try { y = new Double( scan.next() ).doubleValue(); }
         catch( java.lang.NumberFormatException ex ) { y = -999.999; }
      System.out.println( line + "\t=> " + code + "\t| " + x + "\t| " + y );
      scan.close();
   }

   static void loop( String line ) {
      Scanner scan = new Scanner( line );
      scan.useDelimiter( "," );
      while( scan.hasNext() ) {
         boolean db = scan.hasNextDouble();
         String code = scan.next();
         System.out.print( "[" + ( db ? '+' : '-' ) + "] " + code + " | " );
      }
      System.out.println();
      scan.close();
   }

   public static void main( String args[] ) {
      Locale lc = Locale.getDefault();
      String ls = lc.toString();
      if( args.length > 0 ) {
         lc = new Locale( "en", "US" ); 
         Locale.setDefault( lc );
      }
      System.out.println( "locale: " + ls + " => " + lc );
/*    Locale [] alc = Locale.getAvailableLocales();
      for( Locale l : alc ) // просмотреть все локали
         System.out.print( l + " " );
      System.out.println(); */
      for( String s : tests ) {
         parse( s );
         loop(s );
      }
   }
}
Здесь, собственно, набор String tests (похожих по виду на те, что в задаче) последовательно прогоняется через 2 разных парсера :
- parse - считывающий последовательно символьные токены (лексемы), а потом, если это возможно, 2-ю и 3-ю лексему преобразующий в double;
- loop - просто пытающийся в цикле перебрать все токены, и полагающийся в преобразованиях на то "само" ... что обычно прописывается в любом учебнике по Java:

Код: Выделить всё

if( scan.hasNextDouble() ) 
   x = scan.nextDouble();
И вот это, второе - не работает!
Не работает потому, что scan.hasNextDouble() может быть false для совершенно корректных вещественных изображений ... в зависимости от установленной локализации. :-o
Т.е. в дефаултной установке англоязычного Linux всё будет работать ОК, а как только вы его перенесёте на Linux китайский (... или даже русский ;-) ) - всё разлетится вдребезги! (отличная перспектива, особенно в свете коммерческих экспортных поставок :lol: ).

А теперь смотрим, что оно так и есть:

Код: Выделить всё

[Olej@modules pcs]$ java sctest 
locale: ru_RU => ru_RU
AB13 0JW,+57.1,-2.23,+38,+38,	=> AB13 0JW	| 57.1	| -2.23
[-] AB13 0JW | [-] +57.1 | [-] -2.23 | [+] +38 | [+] +38 | 
AB13 xxx,+57.1,-2.23,+38,+38,-1,-1,-1,-1,-1,1	=> AB13 xxx	| 57.1	| -2.23
[-] AB13 xxx | [-] +57.1 | [-] -2.23 | [+] +38 | [+] +38 | [+] -1 | [+] -1 | [+] -1 | [+] -1 | [+] -1 | [+] 1 | 
AB13 0JU,57.112005,-b.234087,385921,385921,	=> AB13 0JU	| 57.112005	| -999.999
[-] AB13 0JU | [-] 57.112005 | [-] -b.234087 | [+] 385921 | [+] 385921 | 
1.1, 4, 5.6,2,-4,-3.3	=> 1.1	| 4.0	| 5.6
[-] 1.1 | [-]  4 | [-]  5.6 | [+] 2 | [+] -4 | [-] -3.3 | 
Это локаль ru_RU. Видно, что:
- корректные символьные изображения вещественных чисел признаются или нет таковыми ([+] или [-]) в совершенно хаотическом порядке;
- ещё одна особенность, если в последовательности ...,123,... стоит пробел до или после ',' - это hasNextDouble() не признаёт [-] :shock:

А теперь меняем локаль:

Код: Выделить всё

[Olej@modules pcs]$ java sctest !
locale: ru_RU => en_US
AB13 0JW,+57.1,-2.23,+38,+38,	=> AB13 0JW	| 57.1	| -2.23
[-] AB13 0JW | [+] +57.1 | [+] -2.23 | [+] +38 | [+] +38 | 
AB13 xxx,+57.1,-2.23,+38,+38,-1,-1,-1,-1,-1,1	=> AB13 xxx	| 57.1	| -2.23
[-] AB13 xxx | [+] +57.1 | [+] -2.23 | [+] +38 | [+] +38 | [+] -1 | [+] -1 | [+] -1 | [+] -1 | [+] -1 | [+] 1 | 
AB13 0JU,57.112005,-b.234087,385921,385921,	=> AB13 0JU	| 57.112005	| -999.999
[-] AB13 0JU | [+] 57.112005 | [-] -b.234087 | [+] 385921 | [+] 385921 | 
1.1, 4, 5.6,2,-4,-3.3	=> 1.1	| 4.0	| 5.6
[+] 1.1 | [-]  4 | [-]  5.6 | [+] 2 | [+] -4 | [+] -3.3 | 
Теперь всё, что есть цифры - прекрасно распознаётся как цифры!

Это история довольно типична для форматирования и ввода в других языках, в том же C со scan() ... может только не так явно выражена.

Тем не менее, мне куда больше нравится "ручной" способ:
- сосчитать отдельный токен в виде как String ...
- а потом самому его преобразовать в double ... и решить что с ним делать (там в коде показана обработка ошибок преобразования)
- хотя бы и из-за проблемы с ведущими и завершающими пробелами.
Вложения
pcs.tgz
(8.83 КБ) 260 скачиваний

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: java.выбор_коллекции

Непрочитанное сообщение Olej » 07 янв 2015, 23:07

Olej писал(а): Не работает потому, что scan.hasNextDouble() может быть false для совершенно корректных вещественных изображений ... в зависимости от установленной локализации. :-o
Вопрос с локализациями не безынтересен не только в связи с переносимостью Java-приложений между разными инсталляциями Linux. Он куда шире ... при переносимости между Linux и Windows ... или ещё чем-то из операционных систем:
- везде по описаниям Java фигурирует, что char - это 16-битовое представление символа Unicode...
- но коды Unicode никогда не были 16-битными - они 32-битные (в общем виде), это тип wchar_t POSIX - "широкие символы"...
- 16-бит - это только кодировка UTF-16 для Unicode ... которая принята только в Windows из-за их нужды совместимости с Windows 95 или даже ранее :lol:
- а ещё есть (повсеместно! во всём Linux - везде) внешняя кодировка UTF-8 для Unicode ... и как и во что она трансформируется при чтении UTF-8 извне в String Java?

Вопросы любопытные, потому как то, что Java-приложения "переносимые куда угодно" ;-) - это мы все знаем.
Но это переносимость в смысле того, что оно будет выполняться.
Но вопрос как будет выполняться!
Вместо ожидаемого вывода могут попереть "каракули", а разумный ввод порождать совершенно безумные действия программы... :-o

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: java.выбор_коллекции

Непрочитанное сообщение Olej » 08 янв 2015, 19:09

Olej писал(а): Не работает потому, что scan.hasNextDouble() может быть false для совершенно корректных вещественных изображений ... в зависимости от установленной локализации. :-o
Это было к вопросу что происходит, а теперь - почему так происходит:

Код: Выделить всё

[Olej@modules ~]$ locale -ck LC_NUMERIC
LC_NUMERIC
decimal_point=","
thousands_sep=" "
grouping=3;3
numeric-decimal-point-wc=44
numeric-thousands-sep-wc=160
numeric-codeset="UTF-8"
[Olej@modules ~]$ locale territory
Russia

Код: Выделить всё

[Olej@modules ~]$ locale decimal_point
,
Вот этого 2-го вывода достаточно, думаю, для понимания: во внешнем (символьном) представлении Russian локали для десятичной точки предусмотрена ','.
Я так понимаю (но проверять лень ;-) ), что всё вводилось бы нормально, если бы входные строки были заданы в виде, например:

Код: Выделить всё

AB13 0JW;+57,1;-2,23;+38;+38
В принципе, достаточно 1-го такого оператора в программе, чтобы не иметь с этим проблем:

Код: Выделить всё

Locale.setDefault( Locale.ENGLISH );

Аватара пользователя
Olej
Писатель
Сообщения: 21338
Зарегистрирован: 24 сен 2011, 14:22
Откуда: Харьков
Контактная информация:

Re: java.выбор_коллекции

Непрочитанное сообщение Olej » 08 янв 2015, 19:19

Olej писал(а):

Код: Выделить всё

[Olej@modules ~]$ locale -ck LC_NUMERIC
LC_NUMERIC
decimal_point=","
thousands_sep=" "
grouping=3;3
numeric-decimal-point-wc=44
numeric-thousands-sep-wc=160
numeric-codeset="UTF-8"
[Olej@modules ~]$ locale territory
Russia

Код: Выделить всё

[Olej@modules ~]$ locale decimal_point
,
Вот этого 2-го вывода достаточно, думаю, для понимания: во внешнем (символьном) представлении Russian локали для десятичной точки предусмотрена ','.
Другое дело, что в Java к локалям должно быть ... особо трепетное :lol: отношение:
- Java-апплеты, например, должны выполняться в непредсказуемой среде ...
- и нельзя полагаться на переменные окружения (что делают C, C++, Go и т.д. код) вида LC_NUMERICAL
Это легко видеть, что изменяя локализацию терминала мы не влияем на локаль Java приложения:

Код: Выделить всё

[Olej@modules pcs]$ LC_NUMERIC=POSIX; java sctest
locale: ru_RU => ru_RU
AB13 0JW,+57.1,-2.23,+38,+38,	=> AB13 0JW	| 57.1	| -2.23
[-] AB13 0JW | [-] +57.1 | [-] -2.23 | [+] +38 | [+] +38 | 
AB13 xxx,+57.1,-2.23,+38,+38,-1,-1,-1,-1,-1,1	=> AB13 xxx	| 57.1	| -2.23
[-] AB13 xxx | [-] +57.1 | [-] -2.23 | [+] +38 | [+] +38 | [+] -1 | [+] -1 | [+] -1 | [+] -1 | [+] -1 | [+] 1 | 
AB13 0JU,57.112005,-b.234087,385921,385921,	=> AB13 0JU	| 57.112005	| -999.999
[-] AB13 0JU | [-] 57.112005 | [-] -b.234087 | [+] 385921 | [+] 385921 | 
1.1, 4, 5.6,2,-4,-3.3	=> 1.1	| 4.0	| 5.6
[-] 1.1 | [-]  4 | [-]  5.6 | [+] 2 | [+] -4 | [-] -3.3 | 

Код: Выделить всё

[Olej@modules pcs]$ export LC_NUMERIC=POSIX
[Olej@modules pcs]$ env | grep LC_
LC_NUMERIC=POSIX
[Olej@modules pcs]$ locale decimal_point
.
[Olej@modules pcs]$ java sctest
locale: ru_RU => ru_RU
AB13 0JW,+57.1,-2.23,+38,+38,	=> AB13 0JW	| 57.1	| -2.23
[-] AB13 0JW | [-] +57.1 | [-] -2.23 | [+] +38 | [+] +38 | 
AB13 xxx,+57.1,-2.23,+38,+38,-1,-1,-1,-1,-1,1	=> AB13 xxx	| 57.1	| -2.23
[-] AB13 xxx | [-] +57.1 | [-] -2.23 | [+] +38 | [+] +38 | [+] -1 | [+] -1 | [+] -1 | [+] -1 | [+] -1 | [+] 1 | 
AB13 0JU,57.112005,-b.234087,385921,385921,	=> AB13 0JU	| 57.112005	| -999.999
[-] AB13 0JU | [-] 57.112005 | [-] -b.234087 | [+] 385921 | [+] 385921 | 
1.1, 4, 5.6,2,-4,-3.3	=> 1.1	| 4.0	| 5.6
[-] 1.1 | [-]  4 | [-]  5.6 | [+] 2 | [+] -4 | [-] -3.3 | 
Т.е. Locale.getDefault() черпает информацию о локали непосредственно из системы, но не из установленного окружения... как-то так. ;-)

И + к этому на Java включаются вопросы безопасности: нельзя Java-приложению, без особых на то привилегий, изменять окружение target-компьютера.

А в деталях о Locale в Java читайте здесь.
Это весьма новые вещи, многое появилось только с версии SE 7.

Ответить

Вернуться в «Программирование»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 8 гостей