Почему значения из файла не хранятся в атрибутах класса? - PullRequest
0 голосов
/ 27 октября 2018

Я пытаюсь написать код, который читает из файла, в котором перечислены продукты Apple.У меня также есть два класса, которые хранят данные из файла в массиве объектов из этих файлов.

Но всякий раз, когда я пытаюсь получить значения из массива этих объектов, он всегда остается нулевым.

Я думаю, что что-то не так с чтением этих значений из файла, но я не могу определить, какие именно.Кроме того, в настоящее время в моем распоряжении нет отладчика.

Это код, который я использую для чтения файла и сохранения его значений в классах.

Scanner fScanner=null;
        FileWriter fw = null;


        try {
            fScanner = new Scanner(new File("apple.txt"));
        } 
        catch (FileNotFoundException e) {
            System.out.println(e.toString());
        }

        while(fScanner.hasNextLine()) {
            String line = fScanner.nextLine();
            Scanner lineScanner = new Scanner(line);

            if(lineScanner.next().equals("IPHONE")) 
                countIphone++;

            else if(lineScanner.next().equals("IPAD"))
                countIpad++;
        }

            Iphone[] iphone = new Iphone[countIphone];
            Ipad[] ipad = new Ipad[countIpad];

        while(fScanner.hasNextLine()) {
            String line = fScanner.nextLine();
            Scanner lineScanner = new Scanner(line);

            if(lineScanner.next().equals("IPHONE")) {
                for(int i=0;i<countIphone;i++) {
                    iphone[i].setModel("IPHONE"+lineScanner.useDelimiter(",").next());
                    iphone[i].setScreenSize(lineScanner.useDelimiter(",").next());
                    iphone[i].setProcessor(lineScanner.useDelimiter(",").next());
                    iphone[i].setSimType(lineScanner.useDelimiter(",").next());
                    iphone[i].setColor(lineScanner.useDelimiter(",").next());
                    iphone[i].setROM(lineScanner.useDelimiter(",").next());
                    iphone[i].setIs3DTouch(lineScanner.useDelimiter(",").next());
                    iphone[i].setPrice(lineScanner.useDelimiter(",").next());
                }
            }


            else if(lineScanner.next().equals("IPAD")) {
                for(int i=0;i<countIpad;i++) {
                    ipad[i].setModel("IPAD"+lineScanner.useDelimiter(",").next());
                    ipad[i].setScreenSize(lineScanner.useDelimiter(",").next());
                    ipad[i].setProcessor(lineScanner.useDelimiter(",").next());
                    ipad[i].setIsWifi(lineScanner.useDelimiter(",").next());
                    ipad[i].setColor(lineScanner.useDelimiter(",").next());
                    ipad[i].setMemory(lineScanner.useDelimiter(",").next());
                    ipad[i].setPrice(lineScanner.useDelimiter(",").next());
                }
            }

        }

Файл выглядит следующим образом:

IPHONE 7, 4.7, A10, GSM, JET BLACK, 32GB, TRUE, 700
IPAD AIR 2, 9.7, A8, TRUE, SILVER, 64GB, 400
IPHONE SE, 4, A9, CDMA, SILVER, 16GB, FALSE, 490
IPAD PRO, 9.7, A9, TRUE, SPACE GREY, 32GB, 650
IPHONE X, 7, A11, LTE, BLACK, 128GB, TRUE, 999
IPAD PRO X, 12, A12, TRUE, SPACE GREY, 256GB, 700

Ответы [ 2 ]

0 голосов
/ 27 октября 2018

@ dasblinkenlight указал причину, по которой iphone и ipad пусты. Но кое-что не так:

  • Во время итерации for(int i=0;i<countIphone;i++) { ... } iphone[i] равно null. Вы никогда не назначали iphone[i] = new Iphone();. Таким образом, попытка вызвать метод, подобный iphone[i].setModel(...), приведет к NullPointerException. То же самое относится к итерации по ipad.
  • Что-то менее очевидное состоит в том, что при вызове new Scanner(new File("apple.txt")) вы открываете ресурс. Это не закрыто в коде. Если вы все еще хотите использовать Scanner, поработайте с оператором try-resource .

Я хотел бы предложить другой дизайн и использовать nio-API и stream-API .

Сначала я определяю перечисление для известных устройств:

  public enum Device {
    IPHONE, IPAD;

    public static Device byName(String name) {
      return Arrays.stream(values())
        .filter(d -> name.startsWith(d.toString()))
        .findAny()
        .orElseThrow(() -> new IllegalArgumentException(String.format("Unknown device name %s", name)));
    }
  }

Метод Device.byName возвращает первое слово в строке из apple.txt соответствующего Device.

Для каждого устройства я определяю Function, который действует как фабрика. Он берет разделенную строку (разделенную на ", ") из apple.txt и возвращает соответствующий экземпляр.

  private static Function<String[], Iphone> toIphone() {
    return line -> {
      Iphone iphone = new Iphone();
      iphone.setModel(Device.IPHONE.toString() + line[0].split(" ")[1]);
      iphone.setScreenSize(line[1]);
      iphone.setProcessor(line[2]);
      iphone.setSimType(line[3]);
      iphone.setColor(line[4]);
      iphone.setROM(line[5]);
      iphone.setIs3DTouch(line[6]);
      iphone.setPrice(line[7]);
      return iphone;
    };
  }

  private static Function<String[], Ipad> toIpad() {
    return line -> {
      Ipad ipad = new Ipad();
      ipad.setModel(Device.IPAD.toString() + line[0].split(" ")[1]);
      ipad.setScreenSize(line[1]);
      ipad.setProcessor(line[2]);
      ipad.setIsWifi(line[3]);
      ipad.setColor(line[4]);
      ipad.setMemory(line[5]);
      ipad.setPrice(line[6]);
      return ipad;
    };
  }

Чтобы решить, какой метод вызывать, я определяю метод, который принимает строку разбиения, представляющую устройство из apple.txt . Он вызывает Device.byName, передавая первый элемент строки разбиения.

  private static Device classify(String[] device) {
    return Device.byName(device[0]);
  }

Теперь мы можем использовать приведенный выше код для такой обработки файла:

  public static void main(String[] args) throws IOException {
    Map<Device, List<String[]>> devices = Files
      .lines(new File("./apple.txt").toPath())
      .map(l -> l.split(", "))
      .collect(Collectors.groupingBy(Example::classify));

    Iphone[] iphones = devices.get(Device.IPHONE).stream()
      .map(toIphone())
      .toArray(Iphone[]::new);
    Ipad[] ipads = devices.get(Device.IPAD).stream()
      .map(toIpad())
      .toArray(Ipad[]::new);
  }

Используя Files.lines нам не нужно беспокоиться о закрытии чтения File.
Позвонив по номеру Collectors.groupingBy(Example::classify), мы получаем Map от Stream, у которого есть экземпляры Device в качестве ключей. Для Devices возвращается List со всеми соответствующими экземплярами.

0 голосов
/ 27 октября 2018

Ваш алгоритм просматривает файл дважды:

  • Первый while(fScanner.hasNextLine()) цикл подсчитывает количество iphone и ipads в файле
  • Второй while(fScanner.hasNextLine()) цикл читает фактические данные

Проблема в том, что после завершения первого цикла все входные данные были использованы.

Вы можете решить эту проблему, закрыв и снова открыв сканер.Однако в вашем коде есть другие проблемы - циклы for внутри условных выражений предполагают, что все iphones и ipads находятся в файле вместе, хотя это не так.

Вы исправляете это, комбинируя циклы сканирования ииспользуя изменяемый размер контейнера для iPhone и iPad:

List<Iphone> iphones = new ArrayList<>();
List<Ipad> ipads = new ArrayList<>();
...