全包字符集可以避免“ 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,可以让我阅读带有许多不同类型字符的文本文件

谢谢。

Answers:


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

39

从Files.newBufferedReader创建BufferedReader

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起作用了。


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

编写从类中的泛型类型转换的字符串“ 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创建,它仍然可以正常工作。

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编写任何字符串时,我从未遇到过任何异常。这是从java.nio.file.Files.newBufferedWriter(path,options)创建的BufferedWriter发生的奇怪错误。


1
这有点离题,因为OP在谈论阅读而不是写作。由于BufferedWriter.write(int),我遇到了类似的问题-将该int视为字符并将其直接写入流中。解决方法是将其手动转换为字符串,然后写入。
malaverdiere

这是一个可悲的投票表决,汤姆,真不错。我想知道在更高版本的Java中是否已解决此问题。
Ryboflavin


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。

例如:

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,但是您不能百分百确保获得所有可能的字符。


-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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.