Reputation: 906
I have been trying to reduce the length of the way. I represent some integer ID's in my program. For Example
2
3
15
26
63
...
151564852
I would like them to be represented as such (0-9A-Za-z only)
2
3
F
Q
z
...
vDF25a //For example
The approach I thought of is to have 63 if statements which each of the mappings from 0-63 to 0-z respectively and for anything above 64 do a recursion on the value minus 63.
Needless to say, I think my approach is very flawed and impractical. What would be a more appropriate way of doing it?
Update:
Following fge's suggestion I've got the encoder to work correctly, however my decode function only works for up-to length 2 strings, in cases where the string is larger the sum becomes erroneous. For example for 3840 to 3845 this is the output
// Encoded
zw
x
zy
zz
100
// Decoded
3840
3841
3842
3843
124 //Invalid decoding
Here is my code for the decode function
public static int decode(String value)
{
String revStr = new StringBuilder(value).reverse().toString();
int sum = 0;
for (int i=1; i < revStr.length(); i++)
{
for (int j=0; j < ALPHABET.length; j++)
{
if (ALPHABET[j] == revStr.charAt(i))
{
sum += (ALPHABET.length * j) * i;
break;
}
}
}
for (int j=0; j < ALPHABET.length; j++)
{
if (ALPHABET[j] == revStr.charAt(0))
{
sum += j;
break;
}
}
return sum;
}
Upvotes: 5
Views: 23423
Reputation: 688
My thanks to the @fge answer. Using it with some changes I've get it to support much larger integers with BigInteger, added support of negative integers and changed Arrays.binarySearch() with HashMap.
BTW, it should be called base62 encoding because [0-9A-Za-z] contains just 62 chars.
public class Base62{
private static final char[] ALPHABET = new char[ 62 ];
private static final Map<Character, Integer> ALPHABET_MAPPING = new HashMap<>();
private static final BigInteger ENCODE_LENGTH = BigInteger.valueOf( ALPHABET.length );
static{
int position = 0;
// numbers
for( int i = 48; i <= 57; i++ ){
ALPHABET[ position++ ] = (char)i;
}
// uppercase letters
for( int i = 65; i <= 90; i++ ){
ALPHABET[ position++ ] = (char)i;
}
// lowercase letters
for( int i = 97; i <= 122; i++ ){
ALPHABET[ position++ ] = (char)i;
}
for( int i = 0; i < ALPHABET.length; i++ ){
ALPHABET_MAPPING.put( ALPHABET[ i ], i );
}
}
public static String encode( final BigInteger in ){
final List<Character> list = new ArrayList<>();
boolean negative = in.signum() == -1;
BigInteger use;
if( negative ){
use = in.negate();
} else {
use = in;
}
do{
BigInteger[] divisionResultAndReminder = use.divideAndRemainder( ENCODE_LENGTH );
list.add( ALPHABET[ divisionResultAndReminder[ 1 ].intValue() ] );
use = divisionResultAndReminder[ 0 ];
} while( use.equals( BigInteger.ZERO ) == false );
Collections.reverse( list );
char[] res = new char[ list.size() ];
for( int i = 0; i < list.size(); i++ ){
res[ i ] = list.get( i );
}
return ( negative ? "-" : "" ) + new String( res );
}
public static BigInteger decode( final String encoded ){
BigInteger res = BigInteger.ZERO;
char c;
boolean negative;
String use;
if( '-' == encoded.charAt( 0 ) ){
negative = true;
use = encoded.substring( 1 );
} else {
negative = false;
use = encoded;
}
for( int index = 0; index < use.length(); index++ ){
c = use.charAt( index );
res = res.multiply( ENCODE_LENGTH );
res = res.add( BigInteger.valueOf( ALPHABET_MAPPING.get( c ) ) );
}
return negative ? res.negate() : res;
}
}
Upvotes: 1
Reputation: 121810
This is not base64; base64 encodes binary data.
Anyway, you don't need a s*load of if
statements; use an array:
public final class AlphabetEncoder
{
private static final char[] ALPHABET = { '0', '1', '2', ...., 'z' };
private static final int ENCODE_LENGTH = ALPHABET.length;
public static String encode(int victim)
{
final List<Character> list = new ArrayList<>();
do {
list.add(ALPHABET[victim % ENCODE_LENGTH]);
victim /= ENCODE_LENGTH;
} while (victim > 0);
Collections.reverse(list);
return new String(list.toArray(new char[list.size()],
StandardCharsets.UTF_8);
}
public int decode(final String encoded)
{
int ret = 0;
char c;
for (int index = 0; index < encoded.length(); index++) {
c = encoded.charAt(index);
ret *= ENCODE_LENGTH;
ret += Arrays.binarySearch(ALPHABET, c);
}
return ret;
}
}
NOTE ABOUT THE DECODE FUNCTION: it is possible to use Arrays.binarySearch()
here since the alphabet has the nice property of being naturally sorted (0 < 1 < 2 < ... < z). However, a test should probably be added that its return code not be negative!
Upvotes: 5
Reputation: 844
You can refer to already existing logic for converting from Decimal [0-9] to Hexadecimal conversion present in Integer
class and extend the logic for your Base 64 converison. Refer
Integer.toHexString(int i)
This maybe the efficient implementation for conversion.
Upvotes: 4
Reputation: 131
Depending on the language you use, there should already be a module which converts a string from/to base64. Check this other post: Base64 Encoding in Java
Upvotes: 2