Reputation: 1
I am trying to decode a string from base64 to UTF-8 for an assignment.
Not having programmed Java for a while I am probably not using the most efficient method, however I managed to implement a function working 99% correctly.
Decoding the example string in Base64: VGhpcyBpcyBhbiBBcnhhbiBzYW1wbGUgc3RyaW5nIHRoYXQgc2hvdWxkIGJlIGVhc2lseSBkZWNvZGVkIGZyb20gYmFzZTY0LiAgSXQgaW5jbHVkZXMgYSBudW1iZXIgb2YgVVRGOCBjaGFyYWN0ZXJzIHN1Y2ggYXMgdGhlIPEsIOksIOgsIOcgYW5kICYjOTYwOyBjaGFyYWN0ZXJzLg==
Results in: This is an Arxan sample string that should be easily decoded from base64. It includes a number of UTF8 characters such as the ñ, é, è, ç and π characters.
However, in the place of the π should be the π symbol being outputted.
Note that I removed the ; after π in here as it seems Stackoverflow automatically corrected it to π
I have tried many things such as creating a byte array and printing that, but still not working.
I am using Eclipse, can it be that just the output there displays incorrectly?
Does somebody have a suggestion to get this to work?
Thanks, Vincent
Here is my code:
package base64;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
public class Base64 {
public static void main(String[] args) {
//Input strings
String base64 = "VGhpcyBpcyBhbiBBcnhhbiBzYW1wbGUgc3RyaW5nIHRoYXQgc2hvdWxkIGJlIGVhc2lseSBkZWNvZGVkIGZyb20gYmFzZTY0LiAgSXQgaW5jbHVkZXMgYSBudW1iZXIgb2YgVVRGOCBjaGFyYWN0ZXJzIHN1Y2ggYXMgdGhlIPEsIOksIOgsIOcgYW5kICYjOTYwOyBjaGFyYWN0ZXJzLg==";
//String base64 = "YW55IGNhcm5hbCBwbGVhc3U=";
String utf8 = "any carnal pleas";
//Base64 to UTF8
System.out.println("Base64 conversion to UTF8");
System.out.println("-------------------------");
System.out.println("Input base64-string: " + base64);
System.out.println("Output UTF8-string: " + stringFromBase64(base64));
System.out.println();
//UTF8 to Base64
System.out.println("UTF8 conversion to base64");
System.out.println("-------------------------");
System.out.println("Input UTF8-string: " + utf8);
System.out.println("Output base64-string: " + stringToBase64(utf8));
System.out.println();
System.out.println("Pi is π");
}
public static String stringFromBase64(String base64) {
StringBuilder binary = new StringBuilder();
int countPadding = countPadding(base64); //count number of padding symbols in source string
//System.out.println("No of *=* in the input is : " + countPadding);
//System.out.println(base64);
for(int i=0; i<(base64.length()-countPadding); i++)
{
int base64Value = fromBase64(String.valueOf(base64.charAt(i))); //convert Base64 character to Int
String base64Binary = Integer.toBinaryString(base64Value); //convert Int to Binary string
StringBuilder base64BinaryCopy = new StringBuilder(); //debugging
if (base64Binary.length()<6) //adds required zeros to make 6 bit string
{
for (int j=base64Binary.length();j<6;j++){
binary.append("0");
base64BinaryCopy.append("0"); //debugging
}
base64BinaryCopy.append(base64Binary); // debugging
} else // debugging
{
base64BinaryCopy.append(base64Binary); //debugging
} // debugging
//System.out.println(base64.charAt(i) + " = " + base64Value + " = " + base64BinaryCopy); //debugging
binary.append(base64Binary);
}
//System.out.println(binary);
//System.out.println(binary.length());
StringBuilder utf8String = new StringBuilder();
for (int bytenum=0;bytenum<(binary.length()/8);bytenum++) //parse string Byte-by-Byte
{
StringBuilder utf8Bit = new StringBuilder();
for (int bitnum=0;bitnum<8;bitnum++){
utf8Bit.append(binary.charAt(bitnum+(bytenum*8)));
}
char utf8Char = (char) Integer.parseInt(utf8Bit.toString(), 2); //Byte to utf8 char
utf8String.append(String.valueOf(utf8Char)); //utf8 char to string and append to final utf8-string
//System.out.println(utf8Bit + " = " + Integer.parseInt(utf8Bit.toString(), 2) + " = " + utf8Char + " = " + utf8String); //debugging
}
return utf8String.toString();
}
public static String stringToBase64(String utf8) {
StringBuilder binary = new StringBuilder();
String paddingString = "";
String paddingSymbols = "";
for(int i=0; i<(utf8.length()); i++)
{
int utf8Value = utf8.charAt(i); //convert utf8 character to Int
String utf8Binary = Integer.toBinaryString(utf8Value); //convert Int to Binary string
StringBuilder utf8BinaryCopy = new StringBuilder(); //debugging
if (utf8Binary.length()<8) //adds required zeros to make 8 bit string
{
for (int j=utf8Binary.length();j<8;j++){
binary.append("0");
utf8BinaryCopy.append("0"); //debugging
}
utf8BinaryCopy.append(utf8Binary); // debugging
} else // debugging
{
utf8BinaryCopy.append(utf8Binary); //debugging
} // debugging
//System.out.println(utf8.charAt(i) + " = " + utf8Value + " = " + utf8BinaryCopy);
binary.append(utf8Binary);
}
if ((binary.length() % 6) == 2) {
paddingString = "0000"; //add 4 padding zeroes
paddingSymbols = "==";
} else if ((binary.length() % 6) == 4) {
paddingString = "00"; //add 2 padding zeroes
paddingSymbols = "=";
}
binary.append(paddingString); //add padding zeroes
//System.out.println(binary);
//System.out.println(binary.length());
StringBuilder base64String = new StringBuilder();
for (int bytenum=0;bytenum<(binary.length()/6);bytenum++) //parse string Byte-by-Byte per 6 bits
{
StringBuilder base64Bit = new StringBuilder();
for (int bitnum=0;bitnum<6;bitnum++){
base64Bit.append(binary.charAt(bitnum+(bytenum*6)));
}
int base64Int = Integer.parseInt(base64Bit.toString(), 2); //Byte to Int
char base64Char = toBase64(base64Int); //Int to Base64 char
base64String.append(String.valueOf(base64Char)); //base64 char to string and append to final Base64-string
//System.out.println(base64Bit + " = " + base64Int + " = " + base64Char + " = " + base64String); //debugging
}
base64String.append(paddingSymbols); //add padding ==
return base64String.toString();
}
public static char toBase64(int a) { //converts integer to corresponding base64 char
String charBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
//charBase64 = new char[]{'A','B','C','D','E','F','G','H','I','J','K','L','M','N'};
return charBase64.charAt(a);
}
public static int fromBase64(String x) { //converts base64 string to corresponding integer
String charBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
return charBase64.indexOf(x);
}
public static int countPadding(String countPadding) { //counts the number of padding symbols in base64 input string
int index = countPadding.indexOf("=");
int count = 0;
while (index != -1) {
count++;
countPadding = countPadding.substring(index + 1);
index = countPadding.indexOf("=");
}
return count;
}
}
Upvotes: 0
Views: 11477
Reputation: 691625
UTF8 is a character encoding that transforms a given char to 1, 2 or more bytes. Your code assumes that each byte should be transformed to a character. That works fine for ASCII characters such as a, b, c that are indeed transformed to a single byte by UTF8, but it doesn't work for characters like PI, which are transformed to a multi-byte sequence.
Your algorithm is awfully inefficient, and I would just ditch it and use a ready-to-use ecnoder/decoder. The JDK 8 comes with one. Guava and commons-codec also do. Your code should be as simple as
String base64EncodedByteArray = "....";
byte[] decodedByteArray = decoder.decode(base64EncodedByteArray);
String asString = new String(decodedByteArray, StandardCharSets.UTF_8);
or, for the other direction:
String someString = "VGhpcyBpcyBhb...";
byte[] asByteArray = someString.getBytes(StandardCharSets.UTF_8);
String base64EncodedByteArray = encoder.encode(asBytArray);
Upvotes: 1