代码之家  ›  专栏  ›  技术社区  ›  DeadlyJesus

UTL_FILE和字符集

  •  4
  • DeadlyJesus  · 技术社区  · 12 年前

    我已经在这件事上工作了好几天,它让我发疯了。
    我有一个oracle程序,它使用 UTL_FILE . 我曾经将我的值存储为NVARCHAR2,并使用 UTL_FILE.PUT_LINE_NCHAR 程序,并将文件写入(记事本++认为是)UTF8。
    该文件随后被另一个程序使用,问题是该程序使用WE8MSWIN1252读取该文件,我无法更改,因为它是旧代码。
    所以我试着用 UTL_FILE.PUT_LINE 但是该文件仍然被认为是UTF8。 我在甲骨文的文档中看到 NVARCHAR2 使用了国家字符集(我的是AL16UTF16),所以我尝试使用 CONVERT 方法如下:

    CONVERT(whatIWantToWrite, 'WE8MSWIN1252', 'AL16UTF16'))
    

    并且引发了ORA-29298字符集不匹配异常。 我不明白,我的 NLS_NCHAR_CHARACTERSET 是AL16UTF16,为什么我不能将其转换为WE8MSWIN1252?
    是否有其他方法可以使用WE8MSWIN1252写入文件?

    3 回复  |  直到 12 年前
        1
  •  6
  •   Alex Poole    12 年前

    这似乎是因为您仍在使用打开文件 fopen_nchar 。如果我这样做:

    create table t42(str nvarchar2(20));
    insert into t42 values ('Hello');
    
    declare
      file utl_file.file_type;
      l_str nvarchar2(20);
    begin
      select str into l_str from t42;
      file := utl_file.fopen('<directory>', 'dummy.dat', 'w', 32767);
      utl_file.put_line(file, convert(l_str, 'WE8MSWIN1252', 'AL16UTF16'));
      utl_file.fclose(file);
    end;
    /
    

    …然后我得到一个包含 䡥汬 ,Linux file 命令报告为 UTF-8 Unicode text ; 记事本++显示 䡥汬 并表示该文件为“ANSI as UTF-8”。

    如果我更改 fopen fopen_字符 :

      file := utl_file.fopen_nchar('CENSYS_EXPORT_DIR', 'dummy.dat', 'w', 32767);
    

    …然后我得到 ORA-29298: Character set mismatch 和一个空文件。

    如果我回到 fopen公司 但将PL/SQL变量更改为 varchar2 :

    declare
      file utl_file.file_type;
      l_str varchar2(20);
    begin
      select str into l_str from t42;
      file := utl_file.fopen('<directory>', 'dummy.dat', 'w', 32767);
      utl_file.put_line(file, convert(l_str, 'WE8MSWIN1252', 'AL16UTF16'));
      utl_file.fclose(file);
    end;
    /
    

    …则文件包含 ¿¿ (在 vim )文件报告为 ISO-8859 text 。但Notepad++显示 ß¿ 并表示该文件是ANSI。

    而不是使用 convert ,哪个Oracle discourages ,您可以将其反弹通过原始:

    declare
      file utl_file.file_type;
      l_str varchar2(20);
    begin
      select str into l_str from t42;
      file := utl_file.fopen('<directory>', 'dummy.dat', 'w', 32767);
      utl_file.put_line(file,
        utl_raw.cast_to_varchar2(utl_raw.convert(utl_raw.cast_to_raw(l_str),
          'ENGLISH_UNITED KINGDOM.WE8MSWIN1252', 'ENGLISH_UNITED KINGDOM.UTF8')));
      utl_file.fclose(file);
    end;
    /
    

    在Linux中,显示为 Hello 文件报告为 ASCII text ; 记事本++显示为 你好 并且再次表示该文件是ANSI。我不清楚这是否能让你达到你需要的位置…当然,你可能需要不同的语言和语言环境。

    但我的数据库字符集是AL32UTF8,我的国家字符集是ALS16UTF16,所以你可能会看到不同的行为;如果您的数据库字符集是WE8MSWIN1252,那么文件也将按此方式创建;从…起 the documentation :

    UTL_FILE 需要打开的文件 UTL_FILE.FOPEN 在文本模式下 在数据库字符集中编码。它需要文件 由打开 UTL_FILE.FOPEN_NCHAR 以UTF8编码 字符集。

        2
  •  0
  •   Wernfried Domscheit    12 年前

    也许您可以选择在将文件写入磁盘后转换文件,例如使用Java工具 Native-to-ASCII 转换器。

    native2ascii -encoding UTF8 my_text_file_utf.txt my_text_file.tmp
    native2ascii -reverse -encoding windows-1252 my_text_file.tmp my_text_file_1252.txt
    
        3
  •  0
  •   Sylwek    10 年前

    您可以使用 dbms_xslprocessor.clob2file .

    declare
      l_str varchar2(20);
    BEGIN
      select str into l_str from t42;
      dbms_xslprocessor.clob2file(to_clob(l_str), 'UTLDIR', 'file.txt', 2000);
    END;
    

    AL16UTF16(csid)=2000 WE8MSWIN1252(csid)=178 得到 CSID :

    SELECT NLS_CHARSET_ID('WE8MSWIN1252') FROM DUAL;