Java Strings breaking file operations

╄→尐↘猪︶ㄣ 提交于 2020-01-05 08:53:10

问题


I have a glitch in my program that causes a question mark (\u003f) to appear in the sixth (index = 5) slot in strings when I encrypt them. Normally, this is reversed upon decryption. However, it is not reversed if I save the string to a file first. I have determined that when I save a string containing Unicode characters to a file, I will not be able to determine the correct length for the file. I have managed to reproduce the glitch in the following function...

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

As an additional note, a failure in file length measurement is also caught.

Here is the Crypto class that I use to encrypt and decrypt Strings...

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

回答1:


The code is correct but almost never works - As a rule of thumb, avoid FileReader and FileWriter and build your own readers/writers using InputStreamReader and OutputStreamWriter which allow you to specify the encoding to use (and hence how to protect 16bit Unicode characters when you write 8bit data).

I use a helper class for this because I need it all the time:

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

The type Encoding is an interface which I implement using one or more enums:

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


来源:https://stackoverflow.com/questions/9569419/java-strings-breaking-file-operations

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!