Шарсе "все включено", щоб уникати "java.nio.charset.MalformedInputException: Вхідна довжина = 1"?


96

Я створюю просту програму підрахунку слів на 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що дозволить мені читати текстові файли з різними типами символів ?

Дякую.

Відповіді:


81

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


44
Я спробував, ISO-8859-1і це працює добре. Я думаю, що це для європейських персонажів, що добре. Я все ще не знаю, чому UTF-16це не працює.
Джонатан Лам,

1
Якщо у вас є Notepad ++, ви можете спробувати відкрити текстовий файл, і він покаже вам кодування файлу в меню. Потім ви можете адаптувати код відповідно, якщо ви завжди отримуєте файл з одного джерела.
JGFMK

@JonathanLam Ну, бо якщо він закодований за допомогою ISO-8859-1, то це не так UTF-16 . Ці кодування абсолютно різні. Файл не може бути обом.
Давуд ібн Карім

@DawoodsaysreinstateMonica Я вважаю, що мав на увазі, що був здивований, що UTF-16 не спрацював так добре, як схоже на загальну інформацію для європейських символів, таких як ISO-8859-1. Але дякую за інформацію (навіть якщо через шість років): P
Джонатан Лам

Звичайно. UTF-16 містить усі європейські символи. Але вони представлені по-різному від ISO-8859-1. У ISO-8859-1 усі символи представлені лише 8 бітами, тому ви обмежені 256 можливими символами. У UTF-16 більшість символів представлені 16 бітами, а деякі символи 32 бітами. Отже, в UTF-16 є набагато більше можливих символів, але для файлу ISO-8859-1 буде потрібно лише вдвічі менше місця, ніж ті самі дані, що використовуються в UTF-16.
Dawood ibn Kareem

40

Створення 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.

Дія за замовчуванням для помилок із неправильним введенням та невідповідними символами - це повідомлення про них.

тоді як останній використовує дію ЗАМІНИ.

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 спрацював.


6

Я також зіткнувся з цим винятком із повідомленням про помилку,

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));

написати рядок "оразг 54", відлитий від загального типу в класі.

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

Цей рядок довжиною 9 містить символи з такими кодовими точками:

111 114 97 122 103 103 9 53 52 10

Однак, якщо BufferedWriter у класі замінено на:

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

він може успішно написати цей рядок без винятків. Крім того, якщо я пишу ту саму String, створену з символів, це все одно працює нормально.

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.
Рибофлавін


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);
    }
}

3

спробуйте це .. у мене була та сама проблема, нижче реалізація працювала для мене

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();
    }

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 ++;
    }
}

Обробник винятків може потенційно зробити while(exception)цикл назавжди, якщо він ніколи не знаходить у масиві діючу кодировку. Обробник винятків повинен відновити, якщо досягнуто кінець масиву і не знайдено робочої коди. Крім того, на момент написання цієї відповіді набрали "-2" голосів. Я проголосував за "-1". Я думаю, що причина, по якій він отримав негативні голоси, полягає в тому, що недостатньо пояснень. Хоча я розумію, що робить код, інші люди можуть цього не робити. Тож коментар на кшталт "ти можеш спробувати щось подібне" може не сподобатися деяким людям.
mvanle

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.