LINUX.ORG.RU

Java - кроссплатформенное чтение UTF-8 файла в одну длинную строку

 ,


0

1

Делал так (взял откуда-то из инета):

File file = new File(fileAbsolutePath);
String s = new Scanner(file).useDelimiter("\\Z").next();
Всего пара строк, в s - весь файл. У меня на windows 1251 по умолчанию, такие файлы читаются нормально. Но на линуксах все не так, да и вообще хочется универсальный вариант. Задача простейшая - выбрать кроссплатформенную кодировку, например UTF-8, и сделать чтобы везде она правильно читалась. Смотрю интернет - как обычно в java: куча вариантов, классов, депрекейтед методов и т.п. Чтение построчное... Вопрос - могу я в 21 веке просто и универсально решить эту сложнейшую сверхзадачу - прочитать текст в строку? По возможности не в 200 строк кода, хотя если в java все так принято, то сойдет хоть как.


Ответ на: комментарий от Deleted

Спасибо, пробую, пока нон-статик метод toString не лезет в статик контекст. Но попробую что-то сделать.

Ivana
() автор топика
Ответ на: комментарий от Deleted

я прочитал по ссылке public static String toString. но мне то мой компилятор пишет именно то что я написал )

Ivana
() автор топика
Ответ на: комментарий от Deleted
    public static void loadFile (String fileName) {
        
        Runnable doIt = new Runnable() {
            public void run() {
                String fileAbsolutePath = CurrentDir() + fileName + ".txt";
                try(InputStream is = new FileInputStream(fileAbsolutePath)) {
                    String s = IOUtils.toString(is, StandardCharsets.UTF_8);
                    System.out.println(s);
                } catch (Throwable ex) {
                    System.out.println(ex.getLocalizedMessage());
                }
            }
        };

        try {
            SwingUtilities.invokeAndWait(doIt);
        } catch(Throwable ex) {
            System.out.println(ex.getLocalizedMessage());
        }
    }

Скорее всего я делаю что-то не так, но компилятор продолжает настаивать на нон-статик методе.

Ivana
() автор топика
Ответ на: комментарий от Ivana

Скорее всего я делаю что-то не так

ты импортируешь что-то не то, мне даже странно такое писать.

Deleted
()
Ответ на: комментарий от Deleted

Возможно я выбрал не то импортировать из списка того. что предложила IDEA. В итоге сердце успокоилось на вот таком коде

                    BufferedReader br = new BufferedReader(
                            new InputStreamReader(
                                    new FileInputStream(fileAbsolutePath), "UTF-8"));
                    final StringBuilder sb = new StringBuilder();
                    String nextString;
                    while ((nextString = br.readLine()) != null) {
                        sb.append(nextString);
                        sb.append("\n");
                    }
                    String s = sb.toString();
Ну и обернуто в invokeLater как в коде выше. Может это и неидиоматично, хотя с другой стороны - все как везде советуют, чтение через БуферРидер, накопление строки через СтрингБилдер.

Ivana
() автор топика
Ответ на: комментарий от Ivana

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

Deleted
()
Ответ на: комментарий от Deleted

Не, ну я сразу же слазил в первоисточник http://www.skipy.ru/technics/encodings.html и вставил строчку br.close(); Или сам бы потом наткнулся на блокированный файл и устранил причину. Я где-то понимаю вашу категоричность, но все равно продолжу играться.

Ivana
() автор топика

Вот минимальный код, читающий файл в строку, используя UTF-8.

        File file = new File("test");
        String fileContents;
        try (InputStream fileStream = new FileInputStream(file);
             InputStream bufStream = new BufferedInputStream(fileStream);
             Reader reader = new InputStreamReader(bufStream, StandardCharsets.UTF_8)) {
            StringBuilder fileContentsBuilder = new StringBuilder();
            char[] buffer = new char[1024];
            int charsRead;
            while ((charsRead = reader.read(buffer)) != -1) {
                fileContentsBuilder.append(buffer, 0, charsRead);
            }
            fileContents = fileContentsBuilder.toString();
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
        System.out.println(fileContents);

Legioner ★★★★★
()
Ответ на: комментарий от Legioner

Через массив чаров волшебного размера 1024... А чем плох вот этот код, который я намеревался оставить:

public static String readFileToString (String fileAbsolutePath) throws IOException {

        BufferedReader br = new BufferedReader(
                new InputStreamReader(
                        new FileInputStream(fileAbsolutePath), "UTF-8"));
        final StringBuilder sb = new StringBuilder();
        String nextString;
        while ((nextString = br.readLine()) != null) {
            sb.append(nextString);
            sb.append("\n");
        }
        br.close();
        return sb.toString();
    }

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

Ivana
() автор топика
Ответ на: комментарий от Ivana

Во-первых, твой код не закрывает файл, если выбрасывается исключение.

Во-вторых он некорректно читает файл. readLine() читает строки с любыми окончаниями: \n, \r, \n\r. Твой код всё это преобразует в \n, т.е. портит исходный текст.

В-третьих он потенциально потребляет больше памяти, когда в файле много длинных строк.

В-четвёртых BufferedReader.readLine использует внутри себя StringBuffer. У этого класса все операции защищены локами, поэтому они могут выполняться дольше аналогичных в StringBuilder-е. В теории JVM может проигнорировать эти локи, но как на практике она себя ведёт, я не знаю.

Legioner ★★★★★
()
Последнее исправление: Legioner (всего исправлений: 1)

Мммм, вот так?

String content = (new BufferedReader(new InputStreamReader(new FileInputStream("myfile")))).lines()
            .collect(() -> new StringBuilder(),
                (b, l) -> b.append(l).append(String.format("%n")),
                (b1, b2) -> b1.append(b2))
            .toString()

Нужна Java8.

Еще. Подключаешь Guava и делаешь:

String text = Resources.toString(Resources.getResource("foo.txt"), Charsets.UTF_8);
DiKeert ★★
()
Последнее исправление: DiKeert (всего исправлений: 3)
Ответ на: комментарий от Legioner

Legioner, спасибо, я не подозревал о стольких подводных камнях. Текст портит, да, это я понимаю, хотя в моем случае не критично. А остальные моменты буду думать тогда. Или пытаться импортировать что-то другое, чтобы самый первый код у меня все-таки вызывал статический toString.

Ivana
() автор топика
Ответ на: комментарий от DiKeert

DiKeert спасибо, попробую. У меня и так требования восьмерки, хотя наверное мое приложение можно было бы написать и на версии меньше. С Guava лаконично смотрится и скорее всего там не дураки - закрывают файлы, лочат что надо и т.п. Сколько она весит только... У меня сейчас все приложение 60 Кб jar-ник, и мне это нравится.

Ivana
() автор топика
Ответ на: комментарий от DiKeert

Имхо только для такой маленькой задачки на фоне всего приложения подключать Guava все-таки оверкилл. Буду пробовать остальные варианты, благо их аж 3 предложено уже.

Ivana
() автор топика
Ответ на: комментарий от Ivana

Аааа, я в первом варианте поспешил и согласился с тем, что предложила IDEA - импортировал sun.misc.IOUtils, а надо было org.apache.commons.io.IOUtils :) Или не импортировать и скопипастить вариант Legioner.

Ivana
() автор топика
Ответ на: комментарий от Legioner

Legioner очередное вам спасибо за код и комментарии. Оставлю в проекте ваш вариант - вы не малы для магии, а сторонние либы для таких мелочей я не хочу.

Ivana
() автор топика
Ответ на: комментарий от Legioner
InputStream fileStream = new FileInputStream(file);
             InputStream bufStream = new BufferedInputStream(fileStream);
             Reader reader = new InputStreamReader(bufStream, StandardCharsets.UTF_8)

три обертки, это вин 8)

Deleted
()
Ответ на: комментарий от Deleted

А что поделаешь, так сделали стандартную библиотеку, максимально разнесли функционал. Зато всё понятно.

Legioner ★★★★★
()
Ответ на: комментарий от Legioner

Тем временем другие маги подсказали вот такое короткое заклинание

        byte[] fileBytes = Files.readAllBytes(Paths.get(fileAbsolutePath));
        String s = new String(fileBytes, StandardCharsets.UTF_8);
У меня работает. Лаконичность подкупает.

Ivana
() автор топика
Ответ на: комментарий от Ivana

Опять же двойной перерасход памяти из-за дублирования. А так нормально. Только Path и File это разные классы, не всегда это может быть удобно.

PS советую подцепить google guava, там очень много полезных методов, она пригождается в любом проекте. С ней это делается ещё проще: Files.toString(file, UTF_8) (класс Files из guava).

Legioner ★★★★★
()
Ответ на: комментарий от Legioner

советую подцепить google guava,

ТС commons-io не осилил, гуава ему вообще плохо сделает.

ps. Files.readAllBytes оптимальнее гуавы т.к. аллоцирует массив на весь файл сразу, а не по мере наполнения

Deleted
()
Последнее исправление: Deleted (всего исправлений: 1)
Ответ на: комментарий от Deleted

Legioner, ну мне не Войну и Мир читать, а потом эту строку все равно в АСТ парсить и она соберется коллектором, так что 2 раза по памяти не критично имхо. Что, говорите? Можно сразу из файла в АСТ парсить, минуя строку? Конечно, вы правы. Да еще и небось библиотечным парсером? Наверное, хотя у того, что я пробовал, выделение слов не такое как я хочу. Но это все имхо некритично, если не Война и Мир. За совет про либы спасибо, но пока хотел обойтись своими велосипедами.

subwoofer, кое-кто (а точнее, никто) в этом топике например не осилил дать это двухстрочное заклинание, а ТС не хочет импортить сторонние либы ради утилитарной подзадачи. Так что умерьте ваши понты, выглядит смешно.

Ivana
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.