【问题标题】:Byte order mark screws up file reading in Java字节顺序标记搞砸了 Java 中的文件读取
【发布时间】:2010-12-22 13:38:21
【问题描述】:

我正在尝试使用 Java 读取 CSV 文件。一些文件的开头可能有一个字节顺序标记,但不是全部。如果存在,字节顺序会与第一行的其余部分一起被读取,从而导致字符串比较出现问题。

有没有一种简单的方法可以在字节顺序标记出现时跳过它?

【问题讨论】:

标签: java utf-8 byte-order-mark


【解决方案1】:

IMO 给出的答案都不是真正令人满意的。只是跳过 BOM,然后以当前平台的默认编码读取流的其余部分,这绝对是错误的。请记住:Unix/Linux 和 windows 上的平台默认值不同:前者是 UTF-8,后者是 ANSI。这种解决方案仅在流的其余部分(在 BOM 之后)仅包含 7 位 ASCII 字符时才有效(我承认,在大多数程序员附近的文件中,例如配置是正确的)。但是一旦出现非 ASCII 字符,这种方法就会失败。

这就是为什么所有可以将字节数组/流转换为字符串(反之亦然)的 java 类/方法都有第二个参数来指示要使用的编码(Reader、Writer、Scanner、String.getBytes() 等。 )。

世界上有很多字符编码,不仅仅是 UTF-xx。而且 - 在今年 2021 年 - 最终用户应用程序之间仍然存在很多编码问题,特别是如果它们在不同平台(iOS、Windows、Unix)上运行。所有这些问题的存在只是因为程序员懒得学习字符编码的工作原理。

因此,绝对必须首先评估要使用的编码,然后使用找到的编码执行字符串/流转换。第一步是查阅相应的规范。只有当您无法确定在读取流时遇到哪种编码时,您必须自己评估它。但请注意:这样的评估始终只是“最佳猜测”,没有一种算法可以涵盖所有可能性。

从这个意义上说,Lee 从 2021 年 2 月 6 日起的答案(和编码示例)是 IMO 最好的答案,但如果没有 BOM,他会回退到 UTF-8。

【讨论】:

    【解决方案2】:

    这是我在大多数字符集中读取 csv 文件的代码。它应该涵盖 99% 的情况。

            try(InputStream inputStream = new FileInputStream(csvFile);){
                BOMInputStream bomInputStream = new BOMInputStream(inputStream ,ByteOrderMark.UTF_8, ByteOrderMark.UTF_16LE, ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_32LE, ByteOrderMark.UTF_32BE);
                Charset charset;
                if(!bomInputStream.hasBOM()) charset = StandardCharsets.UTF_8;
                else if(bomInputStream.hasBOM(ByteOrderMark.UTF_8)) charset = StandardCharsets.UTF_8;
                else if(bomInputStream.hasBOM(ByteOrderMark.UTF_16LE)) charset = StandardCharsets.UTF_16LE;
                else if(bomInputStream.hasBOM(ByteOrderMark.UTF_16BE)) charset = StandardCharsets.UTF_16BE;
                else { throw new Exception("The charset of the file " + csvFile + " is not supported.");}
                
                try(Reader streamReader = new InputStreamReader(bomInputStream, charset);
                    BufferedReader bufferedReader = new BufferedReader(streamReader);) {
                    for(String line; (line = bufferedReader.readLine()) != null; ) {
                        String[] columns = line.split(",");
                 //read csv columns
                }
            }
    

    【讨论】:

    • IMO 最佳答案(和编码示例),但如果没有 BOM,它会回退到 UTF-8。另请参阅下面我的一般回答。
    【解决方案3】:

    NotePad++ 是一个很好的将 UTF-8 编码转换为 UTF-8(BOM) 编码的工具。

    https://notepad-plus-plus.org/downloads/

    UTF8BOMTester.java

    public class UTF8BOMTester {
    
    public static void main(String[] args) throws FileNotFoundException, IOException {
        // TODO Auto-generated method stub
        File file = new File("test.txt");
        boolean same = UTF8BOMInputStream.isSameEncodingType(file);
        System.out.println(same);
        if (same) {
            UTF8BOMInputStream is = new UTF8BOMInputStream(file);
            BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
            System.out.println(br.readLine());
        }
    
    }
    
    static void bytesPrint(byte[] b) {
        for (byte a : b)
            System.out.printf("%x ", a);
    }}
    

    UTF8BOMInputStream.java

    public class UTF8BOMInputStream extends InputStream {
    
    byte[] SYMBLE_BOM = { (byte) 0xEF, (byte) 0xBB, (byte) 0xBF };
    FileInputStream fis;
    final boolean isSameEncodingType;
    public UTF8BOMInputStream(File file) throws IOException {
        FileInputStream fis=new FileInputStream(file);
        byte[] symble=new byte[3];
        fis.read(symble);
        bytesPrint(symble);
        isSameEncodingType=isSameEncodingType(symble);
        if(isSameEncodingType)
            this.fis=fis;
        else
            this.fis=null;
        
    }
    
    @Override
    public int read() throws IOException {
        return fis.read();
    }
    
    void bytesPrint(byte[] b) {
        for (byte a : b)
            System.out.printf("%x ", a);
    }
    
    boolean bytesCompare(byte[] a, byte[] b) {
        if (a.length != b.length)
            return false;
    
        for (int i = 0; i < a.length; i++) {
            if (a[i] != b[i])
                return false;
        }
        return true;
    }
    boolean isSameEncodingType(byte[] symble) {
        return bytesCompare(symble,SYMBLE_BOM);
    }
    public static boolean isSameEncodingType(File file) throws IOException {
        return (new UTF8BOMInputStream(file)).isSameEncodingType;
    }
    

    【讨论】:

      【解决方案4】:

      我遇到了同样的问题,因为我没有阅读一堆文件,所以我做了一个更简单的解决方案。我认为我的编码是 UTF-8,因为当我在此页面的帮助下打印出有问题的字符时:Get unicode value of a character 我发现它是\ufeff。我使用代码 System.out.println( "\\u" + Integer.toHexString(str.charAt(0) | 0x10000).substring(1) ); 打印出违规的 unicode 值。

      一旦我得到了有问题的 unicode 值,我就在我继续阅读之前将它替换到文件的第一行。该部分的业务逻辑:

      String str = reader.readLine().trim();
      str = str.replace("\ufeff", "");
      

      这解决了我的问题。然后我就可以毫无问题地继续处理文件了。我添加了trim(),以防出现前导或尾随空格,您可以根据您的具体需求执行或不执行此操作。

      【讨论】:

      • 这对我不起作用,但我使用了 .replaceFirst("\u00EF\u00BB\u00BF", "") 。
      【解决方案5】:

      @rescdsk 已经提到了Apache Commons IO 库的BOMInputStream,但我没有看到它提到如何在没有 BOM 的情况下获得InputStream

      这是我在 Scala 中的做法。

       import java.io._
       val file = new File(path_to_xml_file_with_BOM)
       val fileInpStream = new FileInputStream(file)   
       val bomIn = new BOMInputStream(fileInpStream, 
               false); // false means don't include BOM
      

      【讨论】:

      • 单参数构造函数:public BOMInputStream(InputStream delegate) { this(delegate, false, ByteOrderMark.UTF_8); }。它默认排除UTF-8 BOM
      • 好点,弗拉基米尔。我在其文档中看到了这一点 - commons.apache.org/proper/commons-io/javadocs/api-2.2/org/…: Constructs a new BOM InputStream that excludes a ByteOrderMark.UTF_8 BOM.
      【解决方案6】:

      要简单地从您的文件中删除 BOM 字符,我建议使用 Apache Common IO

      public BOMInputStream(InputStream delegate,
                    boolean include)
      Constructs a new BOM InputStream that detects a a ByteOrderMark.UTF_8 and optionally includes it.
      Parameters:
      delegate - the InputStream to delegate to
      include - true to include the UTF-8 BOM or false to exclude it
      

      将 include 设置为 false,您的 BOM 字符将被排除。

      【讨论】:

        【解决方案7】:

        更简单的解决方案:

        public class BOMSkipper
        {
            public static void skip(Reader reader) throws IOException
            {
                reader.mark(1);
                char[] possibleBOM = new char[1];
                reader.read(possibleBOM);
        
                if (possibleBOM[0] != '\ufeff')
                {
                    reader.reset();
                }
            }
        }
        

        使用示例:

        BufferedReader input = new BufferedReader(new InputStreamReader(new FileInputStream(file), fileExpectedCharset));
        BOMSkipper.skip(input);
        //Now UTF prefix not present:
        input.readLine();
        ...
        

        它适用于所有 5 种 UTF 编码!

        【讨论】:

        • 非常好的安德烈。但是你能解释一下它为什么有效吗?模式 0xFEFF 如何成功匹配似乎具有不同模式和 3 个字节而不是 2 个字节的 UTF-8 文件?该模式如何同时匹配 UTF16 和 UTF32 的字节序?
        • 如您所见 - 我不使用字节流,而是使用预期的字符集打开字符流。因此,如果此流中的第一个字符是 BOM - 我会跳过它。 BOM 对于每种编码可以有不同的字节表示,但这是一个字符。请阅读这篇文章,它对我有帮助:joelonsoftware.com/articles/Unicode.html
        • 不错的解决方案,只需确保检查文件是否为空,以避免在阅读前跳过方法中的 IOException。你可以调用 if (reader.ready()){ reader.read(possibleBOM) ... }
        • 我看到你已经覆盖了 0xFE 0xFF,这是 UTF-16BE 的字节顺序标记。但是,如果前 3 个字节是 0xEF 0xBB 0xEF 怎么办? (UTF-8 的字节顺序标记)。您声称这适用于所有 UTF-8 格式。这可能是真的(我没有测试你的代码),但它是如何工作的?
        • 查看我对 Vahid 的回答:我打开的不是字节流而是字符流并从中读取一个字符。没关系文件使用什么 utf 编码 - bom 前缀可以用不同的字节数表示,但就字符而言,它只是一个字符
        【解决方案8】:

        Apache Commons IO 库有一个 InputStream 可以检测和丢弃 BOM:BOMInputStream (javadoc)

        BOMInputStream bomIn = new BOMInputStream(in);
        int firstNonBOMByte = bomIn.read(); // Skips BOM
        if (bomIn.hasBOM()) {
            // has a UTF-8 BOM
        }
        

        如果你还需要检测不同的编码,它还可以区分各种不同的字节序标记,例如UTF-8 与 UTF-16 大端 + 小端 - 上面的文档链接中的详细信息。然后您可以使用检测到的ByteOrderMark 选择Charset 来解码流。 (如果您需要所有这些功能,可能有一种更简化的方法来执行此操作 - 也许是 BalusC 答案中的 UnicodeReader?)。请注意,一般来说,检测某些字节的编码方式不是很好,但如果流以 BOM 开头,这显然会有所帮助。

        编辑:如果需要检测UTF-16、UTF-32等格式的BOM,那么构造函数应该是:

        new BOMInputStream(is, ByteOrderMark.UTF_8, ByteOrderMark.UTF_16BE,
                ByteOrderMark.UTF_16LE, ByteOrderMark.UTF_32BE, ByteOrderMark.UTF_32LE)
        

        支持@martin-charlesworth 的评论:)

        【讨论】:

        • 只是跳过 BOM。应该是 99% 用例的完美解决方案。
        • 我成功使用了这个答案。但是,我会恭敬地添加 boolean 参数以指定是包含还是排除 BOM。示例:BOMInputStream bomIn = new BOMInputStream(in, false); // don't include the BOM
        • 我还要补充一点,这只检测 UTF-8 BOM。如果要检测所有 utf-X BOM,则需要将它们传递给 BOMInputStream 构造函数。 BOMInputStream bomIn = new BOMInputStream(is, ByteOrderMark.UTF_8, ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_16LE, ByteOrderMark.UTF_32BE, ByteOrderMark.UTF_32LE);
        • 至于@KevinMeredith的评论,我想强调的是,带有布尔值的构造函数更清晰,但默认构造函数已经摆脱了UTF-8 BOM,正如JavaDoc所建议的那样:BOMInputStream(InputStream delegate) Constructs a new BOM InputStream that excludes a ByteOrderMark.UTF_8 BOM.
        • 跳过可以解决我的大部分问题。如果我的文件以 BOM UTF_16BE 开头,我可以通过跳过 BOM 并将文件读取为 UTF_8 来创建 InputReader 吗?到目前为止它有效,我想了解是否有任何边缘情况?提前致谢。
        【解决方案9】:

        编辑:我已经在 GitHub 上进行了适当的发布:https://github.com/gpakosz/UnicodeBOMInputStream


        这是我前段时间编写的一个类,我只是在粘贴之前编辑了包名。没什么特别的,它与 SUN 的 bug 数据库中发布的解决方案非常相似。将它合并到您的代码中就可以了。

        /* ____________________________________________________________________________
         * 
         * File:    UnicodeBOMInputStream.java
         * Author:  Gregory Pakosz.
         * Date:    02 - November - 2005    
         * ____________________________________________________________________________
         */
        package com.stackoverflow.answer;
        
        import java.io.IOException;
        import java.io.InputStream;
        import java.io.PushbackInputStream;
        
        /**
         * The <code>UnicodeBOMInputStream</code> class wraps any
         * <code>InputStream</code> and detects the presence of any Unicode BOM
         * (Byte Order Mark) at its beginning, as defined by
         * <a href="http://www.faqs.org/rfcs/rfc3629.html">RFC 3629 - UTF-8, a transformation format of ISO 10646</a>
         * 
         * <p>The
         * <a href="http://www.unicode.org/unicode/faq/utf_bom.html">Unicode FAQ</a>
         * defines 5 types of BOMs:<ul>
         * <li><pre>00 00 FE FF  = UTF-32, big-endian</pre></li>
         * <li><pre>FF FE 00 00  = UTF-32, little-endian</pre></li>
         * <li><pre>FE FF        = UTF-16, big-endian</pre></li>
         * <li><pre>FF FE        = UTF-16, little-endian</pre></li>
         * <li><pre>EF BB BF     = UTF-8</pre></li>
         * </ul></p>
         * 
         * <p>Use the {@link #getBOM()} method to know whether a BOM has been detected
         * or not.
         * </p>
         * <p>Use the {@link #skipBOM()} method to remove the detected BOM from the
         * wrapped <code>InputStream</code> object.</p>
         */
        public class UnicodeBOMInputStream extends InputStream
        {
          /**
           * Type safe enumeration class that describes the different types of Unicode
           * BOMs.
           */
          public static final class BOM
          {
            /**
             * NONE.
             */
            public static final BOM NONE = new BOM(new byte[]{},"NONE");
        
            /**
             * UTF-8 BOM (EF BB BF).
             */
            public static final BOM UTF_8 = new BOM(new byte[]{(byte)0xEF,
                                                               (byte)0xBB,
                                                               (byte)0xBF},
                                                    "UTF-8");
        
            /**
             * UTF-16, little-endian (FF FE).
             */
            public static final BOM UTF_16_LE = new BOM(new byte[]{ (byte)0xFF,
                                                                    (byte)0xFE},
                                                        "UTF-16 little-endian");
        
            /**
             * UTF-16, big-endian (FE FF).
             */
            public static final BOM UTF_16_BE = new BOM(new byte[]{ (byte)0xFE,
                                                                    (byte)0xFF},
                                                        "UTF-16 big-endian");
        
            /**
             * UTF-32, little-endian (FF FE 00 00).
             */
            public static final BOM UTF_32_LE = new BOM(new byte[]{ (byte)0xFF,
                                                                    (byte)0xFE,
                                                                    (byte)0x00,
                                                                    (byte)0x00},
                                                        "UTF-32 little-endian");
        
            /**
             * UTF-32, big-endian (00 00 FE FF).
             */
            public static final BOM UTF_32_BE = new BOM(new byte[]{ (byte)0x00,
                                                                    (byte)0x00,
                                                                    (byte)0xFE,
                                                                    (byte)0xFF},
                                                        "UTF-32 big-endian");
        
            /**
             * Returns a <code>String</code> representation of this <code>BOM</code>
             * value.
             */
            public final String toString()
            {
              return description;
            }
        
            /**
             * Returns the bytes corresponding to this <code>BOM</code> value.
             */
            public final byte[] getBytes()
            {
              final int     length = bytes.length;
              final byte[]  result = new byte[length];
        
              // Make a defensive copy
              System.arraycopy(bytes,0,result,0,length);
        
              return result;
            }
        
            private BOM(final byte bom[], final String description)
            {
              assert(bom != null)               : "invalid BOM: null is not allowed";
              assert(description != null)       : "invalid description: null is not allowed";
              assert(description.length() != 0) : "invalid description: empty string is not allowed";
        
              this.bytes          = bom;
              this.description  = description;
            }
        
                    final byte    bytes[];
            private final String  description;
        
          } // BOM
        
          /**
           * Constructs a new <code>UnicodeBOMInputStream</code> that wraps the
           * specified <code>InputStream</code>.
           * 
           * @param inputStream an <code>InputStream</code>.
           * 
           * @throws NullPointerException when <code>inputStream</code> is
           * <code>null</code>.
           * @throws IOException on reading from the specified <code>InputStream</code>
           * when trying to detect the Unicode BOM.
           */
          public UnicodeBOMInputStream(final InputStream inputStream) throws  NullPointerException,
                                                                              IOException
        
          {
            if (inputStream == null)
              throw new NullPointerException("invalid input stream: null is not allowed");
        
            in = new PushbackInputStream(inputStream,4);
        
            final byte  bom[] = new byte[4];
            final int   read  = in.read(bom);
        
            switch(read)
            {
              case 4:
                if ((bom[0] == (byte)0xFF) &&
                    (bom[1] == (byte)0xFE) &&
                    (bom[2] == (byte)0x00) &&
                    (bom[3] == (byte)0x00))
                {
                  this.bom = BOM.UTF_32_LE;
                  break;
                }
                else
                if ((bom[0] == (byte)0x00) &&
                    (bom[1] == (byte)0x00) &&
                    (bom[2] == (byte)0xFE) &&
                    (bom[3] == (byte)0xFF))
                {
                  this.bom = BOM.UTF_32_BE;
                  break;
                }
        
              case 3:
                if ((bom[0] == (byte)0xEF) &&
                    (bom[1] == (byte)0xBB) &&
                    (bom[2] == (byte)0xBF))
                {
                  this.bom = BOM.UTF_8;
                  break;
                }
        
              case 2:
                if ((bom[0] == (byte)0xFF) &&
                    (bom[1] == (byte)0xFE))
                {
                  this.bom = BOM.UTF_16_LE;
                  break;
                }
                else
                if ((bom[0] == (byte)0xFE) &&
                    (bom[1] == (byte)0xFF))
                {
                  this.bom = BOM.UTF_16_BE;
                  break;
                }
        
              default:
                this.bom = BOM.NONE;
                break;
            }
        
            if (read > 0)
              in.unread(bom,0,read);
          }
        
          /**
           * Returns the <code>BOM</code> that was detected in the wrapped
           * <code>InputStream</code> object.
           * 
           * @return a <code>BOM</code> value.
           */
          public final BOM getBOM()
          {
            // BOM type is immutable.
            return bom;
          }
        
          /**
           * Skips the <code>BOM</code> that was found in the wrapped
           * <code>InputStream</code> object.
           * 
           * @return this <code>UnicodeBOMInputStream</code>.
           * 
           * @throws IOException when trying to skip the BOM from the wrapped
           * <code>InputStream</code> object.
           */
          public final synchronized UnicodeBOMInputStream skipBOM() throws IOException
          {
            if (!skipped)
            {
              in.skip(bom.bytes.length);
              skipped = true;
            }
            return this;
          }
        
          /**
           * {@inheritDoc}
           */
          public int read() throws IOException
          {
            return in.read();
          }
        
          /**
           * {@inheritDoc}
           */
          public int read(final byte b[]) throws  IOException,
                                                  NullPointerException
          {
            return in.read(b,0,b.length);
          }
        
          /**
           * {@inheritDoc}
           */
          public int read(final byte b[],
                          final int off,
                          final int len) throws IOException,
                                                NullPointerException
          {
            return in.read(b,off,len);
          }
        
          /**
           * {@inheritDoc}
           */
          public long skip(final long n) throws IOException
          {
            return in.skip(n);
          }
        
          /**
           * {@inheritDoc}
           */
          public int available() throws IOException
          {
            return in.available();
          }
        
          /**
           * {@inheritDoc}
           */
          public void close() throws IOException
          {
            in.close();
          }
        
          /**
           * {@inheritDoc}
           */
          public synchronized void mark(final int readlimit)
          {
            in.mark(readlimit);
          }
        
          /**
           * {@inheritDoc}
           */
          public synchronized void reset() throws IOException
          {
            in.reset();
          }
        
          /**
           * {@inheritDoc}
           */
          public boolean markSupported() 
          {
            return in.markSupported();
          }
        
          private final PushbackInputStream in;
          private final BOM                 bom;
          private       boolean             skipped = false;
        
        } // UnicodeBOMInputStream
        

        而你是这样使用它的:

        import java.io.BufferedReader;
        import java.io.FileInputStream;
        import java.io.InputStreamReader;
        
        public final class UnicodeBOMInputStreamUsage
        {
          public static void main(final String[] args) throws Exception
          {
            FileInputStream fis = new FileInputStream("test/offending_bom.txt");
            UnicodeBOMInputStream ubis = new UnicodeBOMInputStream(fis);
        
            System.out.println("detected BOM: " + ubis.getBOM());
        
            System.out.print("Reading the content of the file without skipping the BOM: ");
            InputStreamReader isr = new InputStreamReader(ubis);
            BufferedReader br = new BufferedReader(isr);
        
            System.out.println(br.readLine());
        
            br.close();
            isr.close();
            ubis.close();
            fis.close();
        
            fis = new FileInputStream("test/offending_bom.txt");
            ubis = new UnicodeBOMInputStream(fis);
            isr = new InputStreamReader(ubis);
            br = new BufferedReader(isr);
        
            ubis.skipBOM();
        
            System.out.print("Reading the content of the file after skipping the BOM: ");
            System.out.println(br.readLine());
        
            br.close();
            isr.close();
            ubis.close();
            fis.close();
          }
        
        } // UnicodeBOMInputStreamUsage
        

        【讨论】:

        • 抱歉,滚动区域太长了,可惜没有附件功能
        • 谢谢 Gregory,这正是我想要的。
        • 这应该在核心 Java API 中
        • 10 年过去了,我仍在为此接受业力:D 我在看你 Java!
        • 赞成,因为答案提供了有关为什么文件输入流默认不提供丢弃 BOM 选项的历史记录。
        【解决方案10】:

        Google Data API 有一个 UnicodeReader,它会自动检测编码。

        您可以使用它来代替InputStreamReader。这是其源代码的 - 略微压缩 - 摘录,非常简单:

        public class UnicodeReader extends Reader {
            private static final int BOM_SIZE = 4;
            private final InputStreamReader reader;
        
            /**
             * Construct UnicodeReader
             * @param in Input stream.
             * @param defaultEncoding Default encoding to be used if BOM is not found,
             * or <code>null</code> to use system default encoding.
             * @throws IOException If an I/O error occurs.
             */
            public UnicodeReader(InputStream in, String defaultEncoding) throws IOException {
                byte bom[] = new byte[BOM_SIZE];
                String encoding;
                int unread;
                PushbackInputStream pushbackStream = new PushbackInputStream(in, BOM_SIZE);
                int n = pushbackStream.read(bom, 0, bom.length);
        
                // Read ahead four bytes and check for BOM marks.
                if ((bom[0] == (byte) 0xEF) && (bom[1] == (byte) 0xBB) && (bom[2] == (byte) 0xBF)) {
                    encoding = "UTF-8";
                    unread = n - 3;
                } else if ((bom[0] == (byte) 0xFE) && (bom[1] == (byte) 0xFF)) {
                    encoding = "UTF-16BE";
                    unread = n - 2;
                } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE)) {
                    encoding = "UTF-16LE";
                    unread = n - 2;
                } else if ((bom[0] == (byte) 0x00) && (bom[1] == (byte) 0x00) && (bom[2] == (byte) 0xFE) && (bom[3] == (byte) 0xFF)) {
                    encoding = "UTF-32BE";
                    unread = n - 4;
                } else if ((bom[0] == (byte) 0xFF) && (bom[1] == (byte) 0xFE) && (bom[2] == (byte) 0x00) && (bom[3] == (byte) 0x00)) {
                    encoding = "UTF-32LE";
                    unread = n - 4;
                } else {
                    encoding = defaultEncoding;
                    unread = n;
                }
        
                // Unread bytes if necessary and skip BOM marks.
                if (unread > 0) {
                    pushbackStream.unread(bom, (n - unread), unread);
                } else if (unread < -1) {
                    pushbackStream.unread(bom, 0, 0);
                }
        
                // Use given encoding.
                if (encoding == null) {
                    reader = new InputStreamReader(pushbackStream);
                } else {
                    reader = new InputStreamReader(pushbackStream, encoding);
                }
            }
        
            public String getEncoding() {
                return reader.getEncoding();
            }
        
            public int read(char[] cbuf, int off, int len) throws IOException {
                return reader.read(cbuf, off, len);
            }
        
            public void close() throws IOException {
                reader.close();
            }
        }
        

        【讨论】:

        • 似乎链接说 Google Data API 已弃用?现在应该去哪里寻找 Google Data API?
        • @XichenLi:GData API 已被弃用。我不打算建议直接使用 GData API(OP 不使用任何 GData 服务),但我打算接管源代码作为您自己实现的示例。这也是为什么我将它包含在我的答案中,准备复制粘贴。
        • 这里有一个错误。 UTF-32LE 大小写是不可达的。为了使(bom[0] == (byte) 0xFF) &amp;&amp; (bom[1] == (byte) 0xFE) &amp;&amp; (bom[2] == (byte) 0x00) &amp;&amp; (bom[3] == (byte) 0x00) 为真,UTF-16LE 大小写 ((bom[0] == (byte) 0xFF) &amp;&amp; (bom[1] == (byte) 0xFE)) 将已经匹配。
        • 由于此代码来自 Google Data API,因此我发布了issue 471 的相关信息。
        【解决方案11】:

        很遗憾没有。您必须识别并跳过自己。 This page 详细说明您必须注意的事项。另请参阅this SO question 了解更多详情。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-11-01
          • 2012-09-20
          • 1970-01-01
          • 2010-09-05
          • 1970-01-01
          相关资源
          最近更新 更多