Полный набор символов, чтобы избежать исключения «java.nio.charset.MalformedInputException: длина ввода = 1»?

97

Я создаю простую программу wordcount на Java, которая читает текстовые файлы каталога.

Однако я продолжаю получать ошибку:

java.nio.charset.MalformedInputException: Input length = 1

из этой строки кода:

BufferedReader reader = Files.newBufferedReader(file,Charset.forName("UTF-8"));

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

Позже в JavaDocs я узнал, что Charsetэто необязательно и используется только для более эффективного чтения файлов, поэтому я изменил код на:

BufferedReader reader = Files.newBufferedReader(file);

Но некоторые файлы все равно закидывают MalformedInputException. Не знаю почему.

Мне было интересно, есть ли комплексное Charsetрешение, которое позволит мне читать текстовые файлы с разными типами символов ?

Спасибо.

Джонатан Лам
источник

Ответы:

82

Вероятно, вы захотите иметь список поддерживаемых кодировок. Для каждого файла попробуйте каждую кодировку по очереди, возможно, начиная с UTF-8. Каждый раз, когда вы поймаете MalformedInputException, попробуйте следующую кодировку.

Дауд ибн Карим
источник
45
Я пробовал, ISO-8859-1и все работает хорошо. Я думаю, это для европейских персонажей, и это нормально. Но я до сих пор не знаю, почему UTF-16не работает.
Джонатан Лам,
1
Если у вас есть Notepad ++, вы можете попробовать открыть текстовый файл, и он сообщит вам кодировку файла в меню. Затем вы можете соответствующим образом адаптировать код, если всегда получаете файл из одного и того же источника.
JGFMK
@JonathanLam Ну, потому что если он закодирован ISO-8859-1, то это не так UTF-16 . Эти кодировки совершенно разные. Файл не может быть тем и другим.
Давуд ибн Карим,
@DawoodsaysreinstateMonica Я думаю, я имел в виду, что был удивлен, что UTF-16 не работал так хорошо, как универсальный для европейских символов, таких как ISO-8859-1. Но спасибо за информацию (даже если шесть лет спустя): П
Джонатан Лам,
Конечно. UTF-16 содержит все европейские символы. Но они представлены иначе, чем ISO-8859-1. В ISO-8859-1 все символы представлены только 8 битами, поэтому вы ограничены 256 возможными символами. В UTF-16 большинство символов представлены 16-битными, а некоторые символы представлены 32-битными. Таким образом, в UTF-16 гораздо больше возможных символов, но для файла ISO-8859-1 потребуется только половина места, чем те же данные будут использовать в UTF-16.
Давуд ибн Карим,
41

Создание BufferedReader из Files.newBufferedReader

Files.newBufferedReader(Paths.get("a.txt"), StandardCharsets.UTF_8);

при запуске приложения может возникнуть следующее исключение:

java.nio.charset.MalformedInputException: Input length = 1

Но

new BufferedReader(new InputStreamReader(new FileInputStream("a.txt"),"utf-8"));

работает хорошо.

Другое дело, что первый использует действие по умолчанию CharsetDecoder.

Действие по умолчанию для ошибок неверного ввода и несопоставимых символов - сообщить о них.

в то время как последний использует действие REPLACE.

cs.newDecoder().onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE)
Синь Ван
источник
29

ISO-8859-1 - это всеобъемлющая кодировка в том смысле, что она гарантированно не генерирует исключение MalformedInputException. Так что это хорошо для отладки, даже если ваш ввод не в этой кодировке. Так:-

req.setCharacterEncoding("ISO-8859-1");

У меня было несколько символов двойной правой кавычки / двойной левой кавычки в моем вводе, и как US-ASCII, так и UTF-8 выдавали на них MalformedInputException, но ISO-8859-1 работал.

Тим Купер
источник
7

Я также столкнулся с этим исключением с сообщением об ошибке,

java.nio.charset.MalformedInputException: Input length = 1
at java.nio.charset.CoderResult.throwException(Unknown Source)
at sun.nio.cs.StreamEncoder.implWrite(Unknown Source)
at sun.nio.cs.StreamEncoder.write(Unknown Source)
at java.io.OutputStreamWriter.write(Unknown Source)
at java.io.BufferedWriter.flushBuffer(Unknown Source)
at java.io.BufferedWriter.write(Unknown Source)
at java.io.Writer.write(Unknown Source)

и обнаружил, что при попытке использовать

BufferedWriter writer = Files.newBufferedWriter(Paths.get(filePath));

для записи преобразования String "orazg 54" из универсального типа в классе.

//key is of generic type <Key extends Comparable<Key>>
writer.write(item.getKey() + "\t" + item.getValue() + "\n");

Эта строка имеет длину 9, содержащую символы со следующими кодовыми точками:

111 114 97 122 103 9 53 52 10

Однако, если BufferedWriter в классе заменяется на:

FileOutputStream outputStream = new FileOutputStream(filePath);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream));

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

String string = new String(new char[] {111, 114, 97, 122, 103, 9, 53, 52, 10});
BufferedWriter writer = Files.newBufferedWriter(Paths.get("a.txt"));
writer.write(string);
writer.close();

Раньше я никогда не сталкивался с какими-либо исключениями при использовании первого BufferedWriter для записи каких-либо строк. Это странная ошибка, которая возникает с BufferedWriter, созданным из java.nio.file.Files.newBufferedWriter (путь, параметры)

Том
источник
1
Это несколько не по теме, поскольку ОП говорил о чтении, а не о письме. У меня была аналогичная проблема из-за BufferedWriter.write (int), который обрабатывает этот int как символ и записывает его непосредственно в поток. Обходной путь - вручную преобразовать его в строку и затем записать.
malaverdiere
Это, к сожалению, недооцененный ответ, действительно хорошая работа, Том. Мне интересно, было ли это решено в более поздних версиях Java.
Рыбофлавин
5

ISO_8859_1 У меня сработало! Я читал текстовый файл со значениями, разделенными запятыми

Шахид Хуссейн Аббаси
источник
4

попробуйте это .. у меня была такая же проблема, ниже реализация работала для меня

Reader reader = Files.newBufferedReader(Paths.get(<yourfilewithpath>), StandardCharsets.ISO_8859_1);

затем используйте Reader где угодно.

foreg:

CsvToBean<anyPojo> csvToBean = null;
    try {
        Reader reader = Files.newBufferedReader(Paths.get(csvFilePath), 
                        StandardCharsets.ISO_8859_1);
        csvToBean = new CsvToBeanBuilder(reader)
                .withType(anyPojo.class)
                .withIgnoreLeadingWhiteSpace(true)
                .withSkipLines(1)
                .build();

    } catch (IOException e) {
        e.printStackTrace();
    }
Вин
источник
3

Я написал следующее, чтобы распечатать стандартный список результатов на основе доступных кодировок. Обратите внимание, что он также сообщает вам, какая строка выходит из строя из номера строки, основанного на 0, в случае, если вы устраняете неполадки, какой символ вызывает проблемы.

public static void testCharset(String fileName) {
    SortedMap<String, Charset> charsets = Charset.availableCharsets();
    for (String k : charsets.keySet()) {
        int line = 0;
        boolean success = true;
        try (BufferedReader b = Files.newBufferedReader(Paths.get(fileName),charsets.get(k))) {
            while (b.ready()) {
                b.readLine();
                line++;
            }
        } catch (IOException e) {
            success = false;
            System.out.println(k+" failed on line "+line);
        }
        if (success) 
            System.out.println("*************************  Successs "+k);
    }
}
EngineerWithJava54321
источник
0

Ну проблема в том, что Files.newBufferedReader(Path path)реализовано так:

public static BufferedReader newBufferedReader(Path path) throws IOException {
    return newBufferedReader(path, StandardCharsets.UTF_8);
}

так что в основном нет смысла указывать, UTF-8если вы не хотите быть описательным в своем коде. Если вы хотите попробовать «более широкую» кодировку, вы можете попробовать StandardCharsets.UTF_16, но вы все равно не можете быть на 100% уверены, что получите все возможные символы.

Франческо Форести
источник
-1

вы можете попробовать что-то подобное или просто скопировать и вставить фрагмент ниже.

boolean exception = true;
Charset charset = Charset.defaultCharset(); //Try the default one first.        
int index = 0;

while(exception) {
    try {
        lines = Files.readAllLines(f.toPath(),charset);
          for (String line: lines) {
              line= line.trim();
              if(line.contains(keyword))
                  values.add(line);
              }           
        //No exception, just returns
        exception = false; 
    } catch (IOException e) {
        exception = true;
        //Try the next charset
        if(index<Charset.availableCharsets().values().size())
            charset = (Charset) Charset.availableCharsets().values().toArray()[index];
        index ++;
    }
}
Pengxiang
источник
Обработчик исключений потенциально может сделать while(exception)цикл навсегда, если он никогда не найдет рабочую кодировку в массиве. Обработчик исключений должен сгенерировать заново, если достигнут конец массива и рабочая кодировка не найдена. Кроме того, на момент написания этот ответ имел «-2» голоса. Я проголосовал за "-1". Я думаю, что причина, по которой он получил отрицательные голоса, заключается в недостаточном объяснении. Я понимаю, что делает код, а другие - нет. Поэтому комментарий типа «вы можете попробовать что-то подобное» может не понравиться некоторым людям.
mvanle,
-1

UTF-8 у меня работает с польскими символами

Адриано
источник