0

在过去的几个小时里,我一直在努力弄清楚我的代码出了什么问题。这段代码一切正常,直到我收到一个包含日文字符的文件。Notepad++ 甚至一些在线实用工具都说文件的编码是UTF-8。记事本说它的UTF-8-BOM。我已经从文件中读取了我的数据并对其进行了处理,最后想将其写入数据库。

我收到错误 org.postgresql.util.PSQLException: ERROR: invalid byte sequence for encoding "UTF8": 0xee My database encoding is UTF8 only..

package citynet.dta.pump;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import org.postgresql.copy.CopyManager;
import org.postgresql.core.BaseConnection;

import citynet.common.ServerException;

public class TestEncoding {

public static void main(String[] args) {
    byte[] bytes = null;
    try {
        //use the below sql to create table 'testtable'
        // create table testtable (text1 character varying, text2 character varying,text3 character varying)
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            DataOutputStream out = new DataOutputStream(baos);

            out.writeBytes("INR,字仮名交じり文,3255104BTK1");

            bytes = baos.toByteArray();
        }
        Class.forName("org.postgresql.Driver");
        Connection c = DriverManager.getConnection("jdbc:postgresql://server:5432/dbname", "username", "password");
        if (bytes != null) {
            try (ByteArrayInputStream input = new ByteArrayInputStream(bytes)) {
                String sql = "COPY testtable  FROM stdin delimiter ','  NULL AS 'null' ENCODING 'UTF8' ";
                BaseConnection pgcon = (BaseConnection) c;
                CopyManager mgr = new CopyManager(pgcon);
                try {
                    mgr.copyIn(sql, input);

                } catch (SQLException ex) {
                    throw new ServerException("Error while copying data in Postgres DB:" + ex);

                }
            }
        }
    } catch (Exception e) {
        System.out.println("Error:" + e);
    }
  }
}
4

1 回答 1

0

问题是DataOutputStream#writeBytes("INR,字仮名交じり文,3255104BTK1")没有做你期望的事情。

  1. 您应该避免使用BaseConnection,因为它是一个内部类。应用程序代码应该使用PGConnection

  2. 这是你如何得到CopyManager

    Connection con = ...;
    PGConnection pgcon = con.unwrap(org.postgresql.PGConnection.class);
    CopyManager mgr = pgcon.getCopyAPI();
    
  3. 您的数据来源可能不同,因此有多种执行方式copyAPI

    如果您想通过自己的代码转换String为字节,那么您需要.UTF-8getBytes

    String sql = "COPY testtable  FROM stdin delimiter ','  NULL AS 'null' ENCODING 'UTF8' ";
    byte[] bytes = "INR,字仮名交じり文,3255104BTK1".getBytes(StandardCharsets.UTF_8);
    mgr.copyIn(sql, new ByteArrayInputStream(bytes));
    

    注意:没有必要close ByteArrayInputStream(参见它的 Javadoc)。

  4. 如果您需要将 CSV 文件流式传输到数据库,您可以使用FileInputStream

    try (InputStream fis = new FileInputStream("file.csv")) {
      mgr.copyIn(sql, fis);
    }
    
  5. 如果您想逐步构建内容,那么您可以使用ByteArrayOutputStream + OutputStreamWriter

    注意:所有行都需要适合内存,否则你会得到OutOfMemoryError.

    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    try (OutputStreamWriter wr = new OutputStreamWriter(baos, StandardCharsets.UTF_8)) {
      // Write 10 rows
      for (int i = 0; i < 10; i++) {
        wr.write("INR,字仮名交じり文,3255104BTK1\n");
      }
    }
    
    String sql = "COPY testtable  FROM stdin delimiter ','  NULL AS 'null' ENCODING 'UTF8'";
    mgr.copyIn(sql, new ByteArrayInputStream(baos.toByteArray()));
    
  6. 另一种选择是使用Reader

    注意:未指定编码,它使用连接默认编码(在 99.42% 的情况下为 utf-8,因为驱动程序默认为 utf-8 连接编码)。

    String sql = "COPY testtable  FROM stdin delimiter ','  NULL AS 'null'";
    mgr.copyIn(sql, new StringReader("INR,字仮名交じり文,3255104BTK1"));
    
  7. 另一种选择是使用copyIn(String sql, ByteStreamWriter from)对某些用例可能更有效的 API(例如,所有数据都在内存中,并且您知道要写入的字节数)

于 2020-04-21T09:05:29.553 回答