task18.task1820

Доброй ночи господа. Столкнулся с такой вот проблемой, Валидатор практически по всем пунктам не доволен моим кодом, не могу понять почему, помогите разобраться, программа работает корректно (Вроде) но по всей видимости я сотворил не пойми что)
Округление чисел
Считать с консоли 2 имени файла.
Первый файл содержит вещественные(дробные) числа, разделенные пробелом. Например, 3.1415.
Округлить числа до целых и записать через пробел во второй файл.
Закрыть потоки.
Принцип округления:
3.49 — 3
3.50 — 4
3.51 — 4
-3.49 — -3
-3.50 — -3
-3.51 — -4

Программа должна два раза считать имена файлов с консоли.+
Для первого файла создай поток для чтения. Для второго — поток для записи.-
Считать числа из первого файла, округлить их и записать через пробел во второй.-
Должны соблюдаться принципы округления, указанные в задании.-
Созданные для файлов потоки должны быть закрыты.-


package com.javarush.task.task18.task1820;

/* 
Округление чисел
*/

import java.io.*;

public class Solution {
    public static void main(String[] args) throws IOException {

        //Считываем 2 имени файла и закрываем поток ридер.
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String s1 = reader.readLine();
        String s2 = reader.readLine();
        reader.close();

        //Открываем поток для чтения и записи с введенными предварительно именами.
        FileInputStream fileInputStream = new FileInputStream(s1);
        FileOutputStream fileOutputStream = new FileOutputStream(s2);

        //Массив для хранения байтов из файла
        byte[] buffer = new byte[fileInputStream.available()];

        //Заполняем массив байтами
        while (fileInputStream.available() > 0) {
            fileInputStream.read(buffer);
        }

        //Закрываем поток чтения.
        fileInputStream.close();

        /*
        Тут я намудрил конечно, но зато работает корректно (Вроде)
        Из массива записываем в Стрингу целые числа по очереди отнимая от них 48
        чтобы работать с "Истинными" значениями, а не их представлениях в байтах.
        Если 46 - ставим точку
        Если 45 - ставим знак "Минус"
        Если 32 - значит пробел и мы переводим стрингу в дабл, округляем ее и записываем в инт
        После чего снова переводим в стнингу и записываем символы в массив чар
        Знаю намудрил, но не суть.
        Далее идет цикл который записывает из массива чар символы в новый файл.
        Добавляем пробел и обнуляем стрингу.
         */
        String s = "";
        double d;
        int b;

        for (int i = 0; i < buffer.length; i++) {
            if (buffer[i] >= 49 && buffer[i] <= 58)
                s += (buffer[i] - 48);
            else if (buffer[i] == 46)
                s += '.';
            else if (buffer[i] == 45)
                s += '-';
            else if (buffer[i] == 32) {
                d = Double.parseDouble(s);
                b = (int) Math.round(d);
                s = ""+b;
                char[] chars = s.toCharArray();
                for (int j = 0; j < chars.length; j++) {
                    fileOutputStream.write(chars[j]);
                }
                fileOutputStream.write(32);
                s = "";
            }
        }

        /*
        Из за рукожопости последнее значение не учитывается, поэтому оно добавляется вне цикла тем же способом.
         */
        d = Double.parseDouble(s);
        b = (int) Math.round(d);
        s = ""+b;
        char[] chars = s.toCharArray();

        for (int i = 0; i < chars.length; i++) {
            fileOutputStream.write(chars[i]);
        }

        //Закрываем поток для чтения.
        fileOutputStream.close();
    }
}
  • ,

javarush.task.task18.task1803 или одна из самых интересных задач

Вот и пришлось мне тут зарегистрироваться, ибо на днях столкнулся с одной из немногих задач, решение которой в голову не приходило вообще. Если честно, для меня это самая сложная задача, которую я встречал за весь курс, что даже аж тут зарегистрироваться пришлось, так как на 3-ий день уже нервы окончательно сдали. Пришлось тупо код скопипастить, но понять все-таки хочется. Это самый легкий код (по крайней мере по объему, в других примерах аж 6 листов люди создают). Вот, собственно, вопросы:

  1. Что значит добавление в map значения 1? (Первый while, условие else)
  2. Почему мы назначаем значение getValue() + 1?
  3. Во втором (отдельном от цикла while) for , мы приравниваем байт к нулю, да еще и очищаем отдельный(непонятно для чего созданный) массив. Зачем?
  4. Соответственно, если значение равно 0, мы добавляем ключ в массив
  5. Ну и последнее, что выводит массив? Количество повторений или сами байты?

Спасибо большое заранее.

    Ввести с консоли имя файла.
    Найти байт или байты с максимальным количеством повторов.
    Вывести их на экран через пробел.
    Закрыть поток ввода-вывода.


public class Solution
    {
        public static void main(String[] args) throws Exception
    {
       BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
       FileInputStream inputStream = new FileInputStream(reader.readLine());
    
    
        HashMap<Integer, Integer> map = new HashMap<>();
    
        while (inputStream.available() > 0)
        {
            int data  = inputStream.read();
    
            if (map.containsKey(data))
            {
                for (HashMap.Entry<Integer, Integer> me : map.entrySet())
                {
                    if (me.getKey().equals(data))
                    {
                        me.setValue(me.getValue() + 1);
                    }
                }
            }
            else {
                map.put(data,1);
            }
        }
    
        int count=0;
        ArrayList<Integer> list = new ArrayList<>();
    
    
        for (HashMap.Entry<Integer, Integer> me : map.entrySet())
        {
            if(count<me.getValue())
            {
                count=me.getValue();
                list.clear();
                list.add(me.getKey());
            }
            else if(count==me.getValue())
            {
                list.add(me.getKey());
            }
        }
        for (Integer aList : list)
        {
            System.out.print(aList + " ");
        }
        inputStream.close();
    }
    }

level09.lesson11.bonus01

Всем привет, при отправке на проверку, программа работает слишком долго. Что не так?
/* Нужно исправить программу, чтобы компилировалась и работала
Задача: Программа вводит два имени файла. И копирует первый файл на место заданное вторым именем.
*/

public class Solution
{
    public static void main(String[] args) throws IOException
    {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

        String sourceFileName = reader.readLine();
        String destinationFileName = reader.readLine();

        java.io.FileInputStream fileInputStream = new java.io.FileInputStream(sourceFileName);
        java.io.FileOutputStream fileOutputStream = new java.io.FileOutputStream(destinationFileName);

        int count = 0;
        while (fileInputStream.available() > 0);
        {
            int data = fileInputStream.read();
            fileOutputStream.write(data);
            count++;
        }

        System.out.println("Скопировано байт " + count);

        fileInputStream.close();
        fileOutputStream.close();
    }
}
  • ,

level18.lesson10.home10 со всеми реализациями не проходит проверку

Доброго времени суток. Вариант программы рабочий, готовые темы форума не помогли, обсуждение на форуме аналогично.

Более подробно с примерами работы и обсуждением на форуме

  • ,

level18.lesson03.task03 работает, но не компилируется на сервере

package com.javarush.test.level18.lesson03.task03;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

/* Самые частые байты
Ввести с консоли имя файла
Найти байт или байты с максимальным количеством повторов
Вывести их на экран через пробел
Закрыть поток ввода-вывода
*/

public class Solution {
    public static void main(String[] args) throws Exception {
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        String fileName = reader.readLine();
        FileInputStream fileInputStream = new FileInputStream(fileName);
        ArrayList<Integer> list = new ArrayList<>();
        while(fileInputStream.available()>0){
            list.add(fileInputStream.read());
        }
        HashMap<Integer, Integer> hm = new HashMap<>();
        Integer am;
        for (Integer i : list) {

            am = hm.get(i);
            hm.put(i, am == null ? 1 : am + 1);
        }
        Iterator<HashMap.Entry<Integer, Integer>> iterator = hm.entrySet().iterator();
        HashMap.Entry<Integer, Integer> tmp = iterator.next();
        int max = tmp.getValue();
        while(iterator.hasNext()){
            HashMap.Entry<Integer, Integer> pair = iterator.next();
            if(pair.getValue()>max){
                max = pair.getValue();
            }
        }
        for (HashMap.Entry<Integer, Integer> pair1 : hm.entrySet())
        {
            if(pair1.getValue()==max){
                System.out.print(pair1.getKey()+" ");
            }
        }
        reader.close();
        fileInputStream.close();
    }
}
  • ,

Побайтовая работа с файлами

Special for Spiker

Начнем'c.
В 18 уровне начались первые задачи побайтного чтения файлов:
Прочитать файл, далее найти минимальные/максимальные байты или вывести в упорядоченном виде и т.п.

Народ тут весьма ушлый. Знают про коллекции и про то, что они могут сортировать, вставлять.
Коллекции — мощный механизм. И многие не применяли их вообще до JavaRush-а.
Оно, конечно, похвально изучать их и пытаться приткнуть куда не попадя.

И так. Возьмем задачу, которой нет в заданиях (чтобы не было спойлеров при решении), но есть сильно похожие:

Ввести с консоли имя файла
Считать все байты из файла.
Не учитывая повторений — отсортировать их по байт-коду в убывающем порядке.
Вывести на экран
Закрыть поток ввода-вывода

Пример байт входного файла
44 83 44

Пример вывода
83 44


Мы дополнительно завели переменные startTime и finishTime — чтобы засечь время выполнения программы.
Для вычисления использовал i3-3GHz/8Gb RAM/HDD WD Blue-1Tb/Win7-64/jdk-8u73-windows-x64

(примеры программ в вариантах 1-2 взяты из форума info.javarush, они чуть модифицированы только для сортировки в возрастающем порядке — то есть они РЕАЛЬНЫЕ!!)

Решаем в лоб:
// Вариант 1. Загоняем в коллекцию и сортируем используя ее метод Collections.sort 
public class Solution {
    public static void main(String[] args) throws Exception {
        FileInputStream inputStream = new FileInputStream(new BufferedReader(new InputStreamReader(System.in)).readLine());
        long startTime = System.currentTimeMillis();
        
        ArrayList<Integer> listData = new ArrayList<Integer>();
        while (inputStream.available() > 0) listData.add(inputStream.read());
        inputStream.close();
        ArrayList<Integer> result = new ArrayList<Integer>(new HashSet<Integer>(listData));
        Collections.sort(result);

        while (!result.isEmpty()) {
            System.out.print(result.get(result.size()-1) + " ");
            result.remove(result.get(result.size()-1));
        }

        long finishTime = System.currentTimeMillis();
        System.out.println("\nвремя работы=" + (finishTime-startTime) + "ms.");
    }
}

Решает все замечательно! Тест (если бы был — прошелся бы на ура).
Но в жизни мало файлов содержащих только строчку «Мама мыла раму».
Давайте скормим нашей программе файл в 46Мб (по нынешним меркам вроде и не особо много).
Что такое, программа выполняется 220 секунд.
Попытка скормить с вечера 1Gb файл (размер MPEG4 фильма не в самом лучшем качестве) не увенчалась успехом.
Программа утром все еще читала — а мне идти на работу уже.

В чем проблема? Наверное в использовании
ArrayList<Integer>
у которого внутри 1 миллиард элементов. Каждый элемент его занимает 16 байт минимум (Заголовок: 8 байт + Поле int: 4 байта + Выравнивание для кратности 8: 4 байта). Итого мы добровольно загоняем в память 16 Gb данных при размере оперативы в 8.

Будем делать лучше. Нырнем в коллекции глубже. И ура, нашлось то, что нам нужно.

Встречаем TreeSet

Это множество:
-не допускает хранение двух одинаковых элементов (а значит мы будем хранить в памяти все 255 элементов, вместо миллиарда!)
-при манипуляциях со своими элементами автоматом упорядочивает (само сортирует — вот он, верх совершенства!)

Получаем:
// Вариант 2. Загоняем в ТreeSet который сам сортирует (лютый win!)
public class Solution {
    public static void main(String[] args) throws Exception {
        FileInputStream inputStream = new FileInputStream(new BufferedReader(new InputStreamReader(System.in)).readLine());

        byte[] arrBytes = new byte[256];
        long startTime = System.currentTimeMillis();

        SortedSet<Integer> list = new TreeSet<Integer>();
        while(inputStream.available()>0) list.add(inputStream.read());
        inputStream.close();

        while (!list.isEmpty())        {
            System.out.print(list.last() + " ");
            list.remove(list.last());
        }

		long finishTime = System.currentTimeMillis();
        System.out.println("\nвремя работы=" + (finishTime-startTime) + "ms.");
    }
}


Имеем на выходе:
46Мб файл 176 секунд.
1Gb файл — 3 часа 5 минут.
Прогресс на лицо. Мы смогли «дождаться» результатов, да и 46Мб файл заметно быстрее обрабатывается.

Идем дальше. Давайте попытаемся отказаться от коллекций (это будет для некоторых мучительно больно). Будем использовать простые массивы (это так примитивно).
Заметим одну важную вещь. Кол-во встречающихся байт можно загнать в массив длиной 256.
Так просто будем увеличивать на единицу соответствующий считанному байту элемент массива.

Массив — побайтно

// Вариант 3. Считываем массив побайтно.
public class Solution {
    public static void main(String[] args) throws Exception {
        FileInputStream inputStream = new FileInputStream(new BufferedReader(new InputStreamReader(System.in)).readLine());

        long[] arrBytes = new long[256];
        long startTime = System.currentTimeMillis();
        
        while (inputStream.available() > 0) arrBytes[inputStream.read()]++;

		inputStream.close();
        // Выводим отсортированный по байт-коду в обратном порядке
        for (long i = 255; i >= 0 ; i--)
            if (arrBytes[(int) i] > 0) System.out.print(i + " ");

			long finishTime = System.currentTimeMillis();
        System.out.println("\nвремя работы=" + (finishTime-startTime) + "ms.");
    }
}


Имеем на выходе:
46Мб файл 158 секунд.
1Gb файл — 2 часа 55 минут.
Опять улучшение, но небольшое. И мы сделали все простыми инструментами. Не использовали микроскоп для забивания гвоздей.

Теперь лирическое отступление.
Вспомним устройство компьютера.
Память ОЗУ (DRAM) где обычно выполняется программа и хранятся переменные имеет высокую скорость доступа, но небольшой размер.
Память на жестком/flash диске (HDD или Flash-накопители) где обычно хранятся файлы, наоборот имеет низкую скорость доступа, но большой размер.
Так что когда мы побайтно читаем 1Gb файл (то есть миллиард раз обращаемся к HDD) — мы тратим много времени на работу с низкоскоростным устройством
(по песчинке перекладываем песок с кузова КамАЗа в песочницу).

Попробуем еще улучшить.

Вывалим сразу ВЕСЬ КамАЗ с песком за один раз!

// Вариант 4. Считываем массив сразу целиком за раз в память.
public class Solution {
    public static void main(String[] args) throws Exception {
        FileInputStream inputStream = new FileInputStream(new BufferedReader(new InputStreamReader(System.in)).readLine());

        long[] arrBytes = new long[256];
        long startTime = System.currentTimeMillis();
        
        byte fileImage[]=new byte[inputStream.available()];
        long fileSize=fileImage.length;
        inputStream.read(fileImage);
        for (int i = 0; i <fileSize ; i++) arrBytes[fileImage[i] & 0b11111111]++;

		inputStream.close();
        // Выводим отсортированный по байт-коду в обратном порядке
        for (long i = 255; i >= 0 ; i--)
            if (arrBytes[(int) i] > 0) System.out.print(i + " ");

		long finishTime = System.currentTimeMillis();
        System.out.println("\nвремя работы=" + (finishTime-startTime) + "ms.");
    }
}

небольшое, но опять таки важное отступление
Заметим:
1. индекс у arrBytes определен в пределах 0..255,
2. fileImage — массив байт, элементы которого имеют значение -128..127
Поэтому для подсчета байт будем использовать конструкцию
arrBytes[fileImage[i] & 0b11111111]++;

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

И так, результаты:
46Мб файл 0.13 секунд (меньше секунды).
1Gb файл — 9 секунд.
Мы сделали это! Мы невероятно круты! Ускорились с 3 часов до 9 секунд.

Все, можно откинуться в кресле и попить чайку. А теперь еще один эксперимент —
попробуем файл в 32 Gb (например, HD фильм). Получим в результате треск
работающего HDD с вываливанием программы в Windows. КамАЗ вывалив кузов с песком сломал песочницу!

Что будем делать? Вспомним еще один факт. Файлы в ОС хранятся обычно порциями (кластерами) по 2-64Кб
(зависит от типа файловой системы, настроек и т.п.). Будем считывать порциями, для примера в 64000 байт.
Попытаемся разгрузить КамАЗ экскаватором достаточно большими порциями:

Используем буфер.
// Вариант 5. Считываем массив кусками.
public class Solution {
    public static void main(String[] args) throws Exception {
        FileInputStream inputStream = new FileInputStream(new BufferedReader(new InputStreamReader(System.in)).readLine());

        long[] arrBytes = new long[256];
        long startTime = System.currentTimeMillis();
        
        int  bufferSize = 64000;
        byte buffer[]   = new byte[64000];

        while (inputStream.available() > 0) {
            if (inputStream.available() < 64000) bufferSize = inputStream.available();
            inputStream.read(buffer, 0, bufferSize );
            for (int i = 0; i <bufferSize ; i++) arrBytes[ buffer[i] & 0b11111111 ]++;
        }

		inputStream.close();
        // Выводим отсортированный по байт-коду в обратном порядке
        for (long i = 255; i >= 0 ; i--)
            if (arrBytes[(int) i] > 0) System.out.print(i + " ");

		long finishTime = System.currentTimeMillis();
        System.out.println("\nвремя работы=" + (finishTime-startTime) + "ms.");
    }
}

В итоге получили:
46Мб файл 0.08 секунд (меньше секунды).
1Gb файл — 0.9 секунд(меньше секунды).
32Gb файл — 31 секунда.

Заметим для 1 Gb файла мы улучшили производительность с нескольких часов до долей секунд!!!

На этом скромном факте закончим эксперимент и улучшение начального кода. Мы достигли прогресса во многом — нас радуют новые показатели расхода памяти и времени работы. Также мы не подтягиваем в данном случае бесполезные коллекции из стандартной библиотеки.

P.S. Кто-то скажет пример надуманный и т.п. Но полно похожих задач — проанализировать огромный объем элементов, имеющих конечное число состояний.
Например изображения (RGB — обычно хранятся в 24 байтах, в нашем случае long[] arrRGB = new long[256*256*256] занял бы в памяти всего 64Мб),
музыка (амплитуда обычно оцифровывается в 16 или 24 бита) или дискретные показатели датчиков и т.п.

Работа с файлами с помощью FileInputStream и FileOutputStream

Работа с файлами с FileInputStream и FileOutputStream.
Читаю книгу Шилдта, дошёл до работы с файлами. В примере сказано что надо запускать программу из командной строки и вводить названия файлов. Но я учусь в IntelliJ. Как осуществить работу с файлами указав путь к ним?
Вот сам пример:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class CopyFile {
    public static void main(String[] args) {
        int i;

        FileInputStream fin;
        FileOutputStream fout;

        if (args.length != 2) {
            System.out.println("Usage: CopyFile From To.");
            return;
        }

        try {
            fin = new FileInputStream(args[0]);
        } catch (FileNotFoundException exc) {
            System.out.println("Input file not found.");
            return;
        }

        try {
            fout = new FileOutputStream(args[1]);
        } catch (FileNotFoundException exc) {
            System.out.println("Error opening output file.");
            try {
                fin.close();
            } catch (IOException exc2) {
                System.out.println("Error closing input file.");
            }
            return;
        }

        try {
            do {
                i = fin.read();
                if (i != -1) fout.write(i);
            } while (i != -1);
        } catch (IOException exc) {
            System.out.println("Fi;e error.");
        }

        try {
            fin.close();
        } catch (IOException exc) {
            System.out.println("Error closing input file.");
        }

        try {
            fout.close();
        } catch (IOException exc) {
            System.out.println("Error closing output file.");
        }
    }
}
  • ,

FileReader считывает не совсем то, что записано в файле

Я никак не могу понять, почему вот такой код
import java.io.*;
import java.util.*;


public class Solution {
    public static void main(String[] args) throws IOException{
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        FileInputStream fis = new FileInputStream(reader.readLine());
        byte[] arr = new byte[10];
        while (fis.available()>0){
            fis.read(arr);
        }
        fis.close();
        for (byte x: arr){
        System.out.println(x);
        }
    }
}


Натравленный на файл со следующим содержанием:

1 2 3 4 5 6 7 8 9 10 11 12 13 14


На выходе дает:
49
52
49
32
49
50
32
49
51
32


Что это за цифры? Откуда они берутся? Я из-за этого не могу сделать некоторые задачи, потому что получаю бред, не подходящий для обработки.
Буду очень благодарен за помощь