【问题标题】:Java Strings breaking file operationsJava字符串破坏文件操作
【发布时间】:2012-03-23 02:07:25
【问题描述】:

我的程序中有一个小故障,当我对字符串进行加密时,它会导致一个问号 (\u003f) 出现在字符串的第六个 (index = 5) 插槽中。通常,这在解密时被反转。但是,如果我先将字符串保存到文件中,则不会反转。我已经确定,当我将包含 Unicode 字符的字符串保存到文件时,我将无法确定文件的正确长度。我设法重现了以下功能中的故障...

public static void testFileIO(String[] args)
{
    System.out.println("TESTING FILE IO FUNCTIONS...");
    try
    {
        String filename = "test.txt";
        String testString = "UB\u4781ERBLAH\u037f\u8746";
        System.out.println("Output: " + testString);
        FileWriter fw = new FileWriter(filename);
        fw.write(testString);
        fw.close();

        FileReader fr = new FileReader(filename);
        int length;
        for(length = 0; fr.read() != -1; length++);
        if(length != testString.length())
            System.out.println("Failure on file length measurement.");
        fr.close();

        fr = new FileReader(filename);
        char[] buffer = new char[length];
        fr.read(buffer);
        String result = new String(buffer);
        fr.close();
        System.out.println("Input: " + result);
        if(result.equals(testString)) System.out.println("SUCCESS!");
        else System.out.println("FAILURE.");
    }
    catch (Throwable e)
    {
        e.printStackTrace();
        System.out.println("FAILURE.");
        return;
    }
}

作为附加说明,文件长度测量失败也被捕获。

这是我用来加密和解密字符串的 Crypto 类...

abstract public class Crypto
{  
    /**
     * Encrypt the plaintext with a bitwise xor cipher
     * @param plainText The plaintext to encrypt
     * @param password The key for the bitwise xor cipher
     * @return Ciphertext yielded by given plaintext and password
     */
    public static String encrypt(String plainText, String key)
    {
        char[] data = plainText.toCharArray();
        Random rand = new Random();
        rand.setSeed(key.hashCode());

        char[] pass = new char[data.length];
        for(int i = 0; i < pass.length; i++)
        {
            pass[i] = (char)rand.nextInt();
        }

        for(int i = 0; i < data.length; i++)
        {
            data[i] ^= pass[i % pass.length];
        }
        return new String(data);
    }

    /**
     * Decrypt an encrypted message using the same key as for encryption
     * @param cipherText The cipherText message to be deciphered
     * @param password The seed for the random generator to get the right keys
     * @return The plaintext message corresponding to 'cipherText'
     */
    public static String decrypt(String cipherText, String key)
    {
        char[] data = cipherText.toCharArray();
        Random rand = new Random();
        rand.setSeed(key.hashCode());

        char[] pass = new char[data.length];// = key.getBytes("ASCII");
        for(int i = 0; i < pass.length; i++)
        {
            pass[i] = (char)rand.nextInt();
        }

        for(int i = 0; i < data.length; i++)
        {
            data[i] ^= pass[i % pass.length];
        }
        return new String(data);
    }
}

【问题讨论】:

  • 您的问题是您忽略了设置流编码。 Java 有一个明显的愚蠢错误,它默认为不可映射的字符提供?
  • 您假设文件长度是字符串长度的 2,但这取决于用于将字符串保存到文件中的编码。
  • 啊! @Luciano:这是我犯的一个愚蠢的错误。我将尝试将其减少为字符串长度 1。
  • @tchrist:非常感谢。我会调查一下并告诉你进展如何。
  • fr.read() 逐字节读取,而 unicode 字符超过一个字节宽。因此,在处理 unicode 时,应始终使用字符流方法而不是字节流方法。

标签: java string encryption encoding file-io


【解决方案1】:

代码是正确的,但几乎从不工作 - 根据经验,避免使用 FileReaderFileWriter 并使用 InputStreamReaderOutputStreamWriter 构建您自己的读取器/写入器,这允许您指定要使用的编码(以及如何在写入 8 位数据时保护 16 位 Unicode 字符)。

我为此使用了一个辅助类,因为我一直都需要它:

private static final String FILE = "file";
private static final String CHARSET = "charset";

public static BufferedReader createReader( File file, Encoding charset ) throws IOException {
    JavaUtils.notNull( FILE, file );
    JavaUtils.notNull( CHARSET, charset );

    FileInputStream stream = null;
    try {
        stream = new FileInputStream( file );
        return createReader( stream, charset );
    } catch( IOException e ) {
        IOUtils.closeQuietly( stream );
        throw e;
    } catch( RuntimeException e ) {
        IOUtils.closeQuietly( stream );
        throw e;
    }
}

public static BufferedReader createReader( InputStream stream, Encoding charset ) throws IOException {
    JavaUtils.notNull( "stream", stream );
    JavaUtils.notNull( "charset", charset );

    try {
        return new BufferedReader( new InputStreamReader( stream, charset.encoding() ) );
    } catch( UnsupportedEncodingException e ) {
        IOUtils.closeQuietly( stream );
        throw new UnknownEncodingException( charset, e );
    } catch( RuntimeException e ) {
        IOUtils.closeQuietly( stream );
        throw e;
    }
}

public static BufferedWriter createWriter( File file, Encoding charset ) throws IOException {
    JavaUtils.notNull( FILE, file );
    JavaUtils.notNull( CHARSET, charset );

    FileOutputStream stream = null;
    try {
        stream = new FileOutputStream( file );
        return new BufferedWriter( new OutputStreamWriter( stream, charset.encoding() ) );
    } catch( UnsupportedEncodingException e ) {
        IOUtils.closeQuietly( stream );
        throw new UnknownEncodingException( charset, e );
    } catch( IOException e ) {
        IOUtils.closeQuietly( stream );
        throw e;
    } catch( RuntimeException e ) {
        IOUtils.closeQuietly( stream );
        throw e;
    }
}

Encoding 类型是我使用一个或多个enums 实现的接口:

public interface Encoding {
    String encoding();
    Charset charset();
}

【讨论】:

  • 我应该使用什么字符集和编码来允许尽可能多的字符?
  • 我目前正在使用您的课程重写测试代码。我会报告任何其他问题。
  • @Aaron Digulla:NetBeans 无法修复某些导入。它说“FILE”、“CHARSET”、“UnknownEncodingException”、“Encoding”和“closeQuietly”不存在。
  • 固定文件和字符集。 closeQuietly 来自commons-ioUnknownEncodingException 只是一个 RuntimException
  • 编码:对于 UTF-8,encoding 返回 "UTF-8"charset() 返回 Charset.forName( encoding() )
猜你喜欢
  • 2015-03-08
  • 2014-03-21
  • 1970-01-01
  • 1970-01-01
  • 2011-11-04
  • 2011-07-10
  • 2017-12-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多