85

私はデータベースキーとしてUUIDを使用することを実験してきました。UUID表現を人間が読める形式に保ちながら、できるだけ少ないバイト数を使用したいと思います。

base64を使用して22バイトに減らし、目的のために保存する必要がないように見える末尾の「==」を削除したと思います。このアプローチに欠陥はありますか?

基本的に、私のテストコードは一連の変換を実行してUUIDを22バイトの文字列に変換し、それをUUIDに変換し直します。

import java.io.IOException;
import java.util.UUID;

public class UUIDTest {

    public static void main(String[] args){
        UUID uuid = UUID.randomUUID();
        System.out.println("UUID String: " + uuid.toString());
        System.out.println("Number of Bytes: " + uuid.toString().getBytes().length);
        System.out.println();

        byte[] uuidArr = asByteArray(uuid);
        System.out.print("UUID Byte Array: ");
        for(byte b: uuidArr){
            System.out.print(b +" ");
        }
        System.out.println();
        System.out.println("Number of Bytes: " + uuidArr.length);
        System.out.println();


        try {
            // Convert a byte array to base64 string
            String s = new sun.misc.BASE64Encoder().encode(uuidArr);
            System.out.println("UUID Base64 String: " +s);
            System.out.println("Number of Bytes: " + s.getBytes().length);
            System.out.println();


            String trimmed = s.split("=")[0];
            System.out.println("UUID Base64 String Trimmed: " +trimmed);
            System.out.println("Number of Bytes: " + trimmed.getBytes().length);
            System.out.println();

            // Convert base64 string to a byte array
            byte[] backArr = new sun.misc.BASE64Decoder().decodeBuffer(trimmed);
            System.out.print("Back to UUID Byte Array: ");
            for(byte b: backArr){
                System.out.print(b +" ");
            }
            System.out.println();
            System.out.println("Number of Bytes: " + backArr.length);

            byte[] fixedArr = new byte[16];
            for(int i= 0; i<16; i++){
                fixedArr[i] = backArr[i];
            }
            System.out.println();
            System.out.print("Fixed UUID Byte Array: ");
            for(byte b: fixedArr){
                System.out.print(b +" ");
            }
            System.out.println();
            System.out.println("Number of Bytes: " + fixedArr.length);

            System.out.println();
            UUID newUUID = toUUID(fixedArr);
            System.out.println("UUID String: " + newUUID.toString());
            System.out.println("Number of Bytes: " + newUUID.toString().getBytes().length);
            System.out.println();

            System.out.println("Equal to Start UUID? "+newUUID.equals(uuid));
            if(!newUUID.equals(uuid)){
                System.exit(0);
            }


        } catch (IOException e) {
        }

    }


    public static byte[] asByteArray(UUID uuid) {

        long msb = uuid.getMostSignificantBits();
        long lsb = uuid.getLeastSignificantBits();
        byte[] buffer = new byte[16];

        for (int i = 0; i < 8; i++) {
            buffer[i] = (byte) (msb >>> 8 * (7 - i));
        }
        for (int i = 8; i < 16; i++) {
            buffer[i] = (byte) (lsb >>> 8 * (7 - i));
        }

        return buffer;

    }

    public static UUID toUUID(byte[] byteArray) {

        long msb = 0;
        long lsb = 0;
        for (int i = 0; i < 8; i++)
            msb = (msb << 8) | (byteArray[i] & 0xff);
        for (int i = 8; i < 16; i++)
            lsb = (lsb << 8) | (byteArray[i] & 0xff);
        UUID result = new UUID(msb, lsb);

        return result;
    }

}

出力:

UUID String: cdaed56d-8712-414d-b346-01905d0026fe
Number of Bytes: 36

UUID Byte Array: -51 -82 -43 109 -121 18 65 77 -77 70 1 -112 93 0 38 -2 
Number of Bytes: 16

UUID Base64 String: za7VbYcSQU2zRgGQXQAm/g==
Number of Bytes: 24

UUID Base64 String Trimmed: za7VbYcSQU2zRgGQXQAm/g
Number of Bytes: 22

Back to UUID Byte Array: -51 -82 -43 109 -121 18 65 77 -77 70 1 -112 93 0 38 -2 0 38 
Number of Bytes: 18

Fixed UUID Byte Array: -51 -82 -43 109 -121 18 65 77 -77 70 1 -112 93 0 38 -2 
Number of Bytes: 16

UUID String: cdaed56d-8712-414d-b346-01905d0026fe
Number of Bytes: 36

Equal to Start UUID? true
4

10 に答える 10

65

私も似たようなことをしようとしていました。6fcb514b-b878-4c9d-95b7-8dc3a7ce6fd8フォームの UUID (Java の標準 UUID ライブラリで生成される)を使用する Java アプリケーションを使用しています。私の場合、この UUID を 30 文字以下にする必要がありました。Base64 を使用しましたが、これらは便利な機能です。解決策がすぐにはわからなかったので、誰かに役立つことを願っています。

使用法:

String uuid_str = "6fcb514b-b878-4c9d-95b7-8dc3a7ce6fd8";
String uuid_as_64 = uuidToBase64(uuid_str);
System.out.println("as base64: "+uuid_as_64);
System.out.println("as uuid: "+uuidFromBase64(uuid_as_64));

出力:

as base64: b8tRS7h4TJ2Vt43Dp85v2A
as uuid  : 6fcb514b-b878-4c9d-95b7-8dc3a7ce6fd8

機能:

import org.apache.commons.codec.binary.Base64;

private static String uuidToBase64(String str) {
    Base64 base64 = new Base64();
    UUID uuid = UUID.fromString(str);
    ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
    bb.putLong(uuid.getMostSignificantBits());
    bb.putLong(uuid.getLeastSignificantBits());
    return base64.encodeBase64URLSafeString(bb.array());
}
private static String uuidFromBase64(String str) {
    Base64 base64 = new Base64(); 
    byte[] bytes = base64.decodeBase64(str);
    ByteBuffer bb = ByteBuffer.wrap(bytes);
    UUID uuid = new UUID(bb.getLong(), bb.getLong());
    return uuid.toString();
}
于 2013-02-21T22:10:53.070 に答える
32

このアプリケーションでは、パディング「==」を安全に削除できます。base-64 テキストをデコードしてバイトに戻した場合、一部のライブラリはそこにあると想定しますが、結果の文字列をキーとして使用しているだけなので問題ありません。

Base-64 のエンコーディング文字は URL セーフである可能性があり、意味不明に見えないため、私は Base-64 を使用します。しかし、Base-85もあります。より多くの記号とコードを 4 バイトを 5 文字として使用するため、テキストを 20 文字まで減らすことができます。

于 2009-04-21T14:46:18.440 に答える
11

これが私のコードです。org.apache.commons.codec.binary.Base64 を使用して、長さが 22 文字の (そして UUID と同じ一意性を持つ) URL セーフな一意の文字列を生成します。

private static Base64 BASE64 = new Base64(true);
public static String generateKey(){
    UUID uuid = UUID.randomUUID();
    byte[] uuidArray = KeyGenerator.toByteArray(uuid);
    byte[] encodedArray = BASE64.encode(uuidArray);
    String returnValue = new String(encodedArray);
    returnValue = StringUtils.removeEnd(returnValue, "\r\n");
    return returnValue;
}
public static UUID convertKey(String key){
    UUID returnValue = null;
    if(StringUtils.isNotBlank(key)){
        // Convert base64 string to a byte array
        byte[] decodedArray = BASE64.decode(key);
        returnValue = KeyGenerator.fromByteArray(decodedArray);
    }
    return returnValue;
}
private static byte[] toByteArray(UUID uuid) {
    byte[] byteArray = new byte[(Long.SIZE / Byte.SIZE) * 2];
    ByteBuffer buffer = ByteBuffer.wrap(byteArray);
    LongBuffer longBuffer = buffer.asLongBuffer();
    longBuffer.put(new long[] { uuid.getMostSignificantBits(), uuid.getLeastSignificantBits() });
    return byteArray;
}
private static UUID fromByteArray(byte[] bytes) {
    ByteBuffer buffer = ByteBuffer.wrap(bytes);
    LongBuffer longBuffer = buffer.asLongBuffer();
    return new UUID(longBuffer.get(0), longBuffer.get(1));
}
于 2013-08-05T11:33:27.780 に答える
8

私はほぼ正確にこれを行っているアプリケーションを持っています。22 文字のエンコードされた UUID。それは正常に動作します。ただし、私がこのようにしている主な理由は、ID が Web アプリの URI で公開されており、URI に表示されるものとして 36 文字は非常に大きいためです。22文字はまだちょっと長いですが、やり遂げます。

このための Ruby コードは次のとおりです。

  # Make an array of 64 URL-safe characters
  CHARS64 = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a + ["-", "_"]
  # Return a 22 byte URL-safe string, encoded six bits at a time using 64 characters
  def to_s22
    integer = self.to_i # UUID as a raw integer
    rval = ""
    22.times do
      c = (integer & 0x3F)
      rval += CHARS64[c]
      integer = integer >> 6
    end
    return rval.reverse
  end

base64 では、URI パス コンポーネントに出現する場合にエスケープする必要のある文字が使用されるため、base64 エンコーディングとはまったく同じではありません。Java の実装はかなり異なる可能性があります。これは、実際に大きな整数ではなく生のバイトの配列を使用する可能性が高いためです。

于 2009-04-24T15:35:22.107 に答える
3

使用している DBMS はわかりませんが、スペースを節約することを懸念している場合は、RAW が最適な方法のようです。すべてのクエリに対して変換することを忘れないでください。そうしないと、パフォーマンスが大幅に低下する危険があります。

しかし、私は尋ねなければなりません: あなたが住んでいるところでバイトは本当にそんなに高価ですか?

于 2009-04-21T14:30:33.830 に答える
1

以下は、UUID (Comb スタイル) に使用するものです。これには、uuid 文字列または uuid タイプを base64 に変換するためのコードが含まれています。私は 64 ビットごとに行うので、等号は扱いません。

ジャワ

import java.util.Calendar;
import java.util.UUID;
import org.apache.commons.codec.binary.Base64;

public class UUIDUtil{
    public static UUID combUUID(){
        private UUID srcUUID = UUID.randomUUID();
        private java.sql.Timestamp ts = new java.sql.Timestamp(Calendar.getInstance().getTime().getTime());

        long upper16OfLowerUUID = this.zeroLower48BitsOfLong( srcUUID.getLeastSignificantBits() );
        long lower48Time = UUIDUtil.zeroUpper16BitsOfLong( ts );
        long lowerLongForNewUUID = upper16OfLowerUUID | lower48Time;
        return new UUID( srcUUID.getMostSignificantBits(), lowerLongForNewUUID );
    }   
    public static base64URLSafeOfUUIDObject( UUID uuid ){
        byte[] bytes = ByteBuffer.allocate(16).putLong(0, uuid.getLeastSignificantBits()).putLong(8, uuid.getMostSignificantBits()).array();
        return Base64.encodeBase64URLSafeString( bytes );
    }
    public static base64URLSafeOfUUIDString( String uuidString ){
    UUID uuid = UUID.fromString( uuidString );
        return UUIDUtil.base64URLSafeOfUUIDObject( uuid );
    }
    private static long zeroLower48BitsOfLong( long longVar ){
        long upper16BitMask =  -281474976710656L;
        return longVar & upper16BitMask;
    }
    private static void zeroUpper16BitsOfLong( long longVar ){
        long lower48BitMask =  281474976710656L-1L;
        return longVar & lower48BitMask;
    }
}
于 2011-09-11T15:36:29.440 に答える