注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

民主与科学

独立之人格,自由之思想

 
 
 

日志

 
 

Android中汉字转拼音的内存优化处理  

2012-10-11 22:14:22|  分类: 问题小汇 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
当我们对中文名字排序时,往往采用是的用其拼音来排序的。Android中通过汉字的unicode编码来找到其对应的拼音。汉字unicode编码和其拼音是放在资源文件chinese_unicode_to__pinyin.txt中。
其结构很简单,比如:
示例1:

3007 ling 

4E00 yi 

4E01 ding 

4E02 kao 

它正好对应java.util.Properties所需要的格式。但是由于它继承于Hashtable,他使用的是哈希桶来把汉字unicode编码和其拼音对应,有很多哈希桶被创建而没实际使用,造成大量的哈希桶浪费,实际占用内存达2M多。
考虑到chinese_unicode_to__pinyin.txt本身的数据结构特点,我们在采用二维数组的索引来对应汉字unicode编码,第一维数组的下标对应汉字unicode编码的高8表,第二维数组的下标对应汉字unicode编码的低8表,第二维数组数存的字符串来对应汉字拼音的引用。如果在存一个汉字的拼音时,前面有汉字的拼音和他一样时,则使用这个该汉字的拼音的引用就可以了,而不必再创建新的字符串。
对于示例1的汉字和拼音就这样存储:

pinyinTable[0x30][0x0f]="ling"

pinyinTable[0x4E][0x00]="yi"

pinyinTable[0x4E][0x01]="ding" 

pinyinTable[0x4E][0x02]="kao"

这样使用MAT查看占用内存就变为1M多,但是实际计算却主要是引用(一个引用占32位,即4个字节)占用的内存比较多,大概是6*(2~12)*4=96k
另外很多汉字的拼音都是相同,所以使用白盒测试显示字符串占用的更少。MAT显示的占用内存仍然有1M,应该是重复计算字符串引起的。
文件ChineseToPinyinUtil.java

import java.io.BufferedInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.HashMap;
import java.util.Properties;

import android.util.Log;

public class ChineseToPinyinUtil {
    /**
     * A hash table contains <Unicode, HanyuPinyin> pairs
     */

    final String noneStr = "none";


    /**
     * Private constructor as part of the singleton pattern.
     */
    private ChineseToPinyinUtil() {
        initializeResource();
    }
    final char pinyinTable[][][]=new char[256][][];
    /**
     * Initialize a hash-table contains <Unicode, HanyuPinyin> pairs
     */
    private void initializeResource() {
        BufferedInputStream bis = null;
        try {
            final String resourceName = "/assets/chinese_unicode_to__pinyin.txt";
             bis = ResourceHelper.getResourceInputStream(resourceName);
             load(bis);
        } catch (FileNotFoundException ex) {

        } catch (IOException ex) {
            
        }
        finally{
        if(bis!=null)
        {
        try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
       bis=null;
        }
        }
    }

    /**
     * @param record
     *            given record string of Hanyu Pinyin
     * @return return true if record is not null and record is not "none0" and
     *         record is not mal-formatted, else return false
     */
    private boolean isValidRecord(String record) {
        final String noneStr = "(none0)";

        if ((null != record) && !record.equals(noneStr)) {
            return true;
        } else {
            return false;
        }
    }

    public String getHanyuPinyinString(String str) {
        if (str == null)
            return null;
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < str.length(); i++) {
            char codePointOfChar = str.charAt(i);

            // fetch from hashtable
            String foundRecord =null;
            if(pinyinTable[(codePointOfChar>>8)&0xFF]!=null
            &&pinyinTable[(codePointOfChar>>8)&0xFF][codePointOfChar&0xFF]!=null)
            {
           
            foundRecord =new String(pinyinTable[(codePointOfChar>>8)&0xFF][codePointOfChar&0xFF]);
            }
            sb.append(isValidRecord(foundRecord) ? foundRecord : codePointOfChar);
        }
        if(sb.length()==0)
        {
        return str;
        }
        return sb.toString();
    }

    /**
     * Singleton factory method.
     * 
     * @return the one and only MySingleton.
     */
    static ChineseToPinyinResource getInstance() {
        return ChineseToPinyinResourceHolder.theInstance;
    }

    /**
     * Singleton implementation helper.
     */
    private static class ChineseToPinyinResourceHolder {
        static final ChineseToPinyinResource theInstance = new ChineseToPinyinResource();
    }

    /**
     * Reads a property list (key and element pairs) from the input
     * byte stream. The input stream is in a simple line-oriented
     * format as specified in
     * {@link #load(java.io.Reader) load(Reader)} and is assumed to use
     * the ISO 8859-1 character encoding; that is each byte is one Latin1
     * character. Characters not in Latin1, and certain special characters,
     * are represented in keys and elements using
     * <a href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.3">Unicode escapes</a>.
     * <p>
     * The specified stream remains open after this method returns.
     *
     * @param      inStream   the input stream.
     * @exception  IOException  if an error occurred when reading from the
     *             input stream.
     * @throws   IllegalArgumentException if the input stream contains a
     *   malformed Unicode escape sequence.
     * @since 1.2
     */
    final private void load(InputStream inStream) throws IOException {
        load0(new LineReader(inStream));
    }

    private void load0 (LineReader lr) throws IOException {
        char[] convtBuf = new char[1024];
        int limit;
        int keyLen;
        int valueStart;
        char c;
        boolean hasSep;
        boolean precedingBackslash;
        int index=0;
        int index0=0;
        int index1=0;
        char chars[]=null;
        HashMap<String,char[]> hasMap=new HashMap<String,char[]>(500);
        while ((limit = lr.readLine()) >= 0) {
            c = 0;
            keyLen = 0;
            valueStart = limit;
            hasSep = false;

   //System.out.println("line=<" + new String(lineBuf, 0, limit) + ">");
            precedingBackslash = false;
            while (keyLen < limit) {
                c = lr.lineBuf[keyLen];
                //need check if escaped.
                if ((c == '=' ||  c == ':') && !precedingBackslash) {
                    valueStart = keyLen + 1;
                    hasSep = true;
                    break;
                } else if ((c == ' ' || c == '\t' ||  c == '\f') && !precedingBackslash) {
                    valueStart = keyLen + 1;
                    break;
                } 
                if (c == '\\') {
                    precedingBackslash = !precedingBackslash;
                } else {
                    precedingBackslash = false;
                }
                keyLen++;
            }
            while (valueStart < limit) {
                c = lr.lineBuf[valueStart];
                if (c != ' ' && c != '\t' &&  c != '\f') {
                    if (!hasSep && (c == '=' ||  c == ':')) {
                        hasSep = true;
                    } else {
                        break;
                    }
                }
                valueStart++;
            }
            String key = loadConvert(lr.lineBuf, 0, keyLen, convtBuf);
            String value = loadConvert(lr.lineBuf, valueStart, limit - valueStart, convtBuf);
            index=Integer.parseInt(key,16);
            index0=(index>>8)&0xFF;
            index1=(index&0xFF);
            if(pinyinTable[index0]==null)
            {
            pinyinTable[index0]=new char[256][];
            }
            chars=value.toCharArray();
            pinyinTable[index0][index1]=hasMap.get(value);
            if(pinyinTable[index0][index1]==null)
            {
            pinyinTable[index0][index1]=chars;
            hasMap.put(value, chars);
            }
        }
        hasMap.clear();
    }

    /* Read in a "logical line" from an InputStream/Reader, skip all comment
     * and blank lines and filter out those leading whitespace characters 
     * (\u0020, \u0009 and \u000c) from the beginning of a "natural line". 
     * Method returns the char length of the "logical line" and stores 
     * the line in "lineBuf". 
     */
    class LineReader {
        public LineReader(InputStream inStream) {
            this.inStream = inStream;
            inByteBuf = new byte[8192]; 
}

        public LineReader(Reader reader) {
            this.reader = reader;
            inCharBuf = new char[8192]; 
}

        byte[] inByteBuf;
        char[] inCharBuf;
        char[] lineBuf = new char[1024];
        int inLimit = 0;
        int inOff = 0;
        InputStream inStream;
        Reader reader;

        int readLine() throws IOException {
            int len = 0;
            char c = 0;

            boolean skipWhiteSpace = true;
            boolean isCommentLine = false;
            boolean isNewLine = true;
            boolean appendedLineBegin = false;
            boolean precedingBackslash = false;
   boolean skipLF = false;

            while (true) {
                if (inOff >= inLimit) {
                    inLimit = (inStream==null)?reader.read(inCharBuf)
                             :inStream.read(inByteBuf);
   inOff = 0;
   if (inLimit <= 0) {
if (len == 0 || isCommentLine) { 
   return -1; 
}
return len;
   }
}     
                if (inStream != null) {
                    //The line below is equivalent to calling a 
                    //ISO8859-1 decoder.
           c = (char) (0xff & inByteBuf[inOff++]);
                } else {
                    c = inCharBuf[inOff++];
                }
                if (skipLF) {
                    skipLF = false;
   if (c == '\n') {
       continue;
   }
}
if (skipWhiteSpace) {
   if (c == ' ' || c == '\t' || c == '\f') {
continue;
   }
   if (!appendedLineBegin && (c == '\r' || c == '\n')) {
continue;
   }
   skipWhiteSpace = false;
   appendedLineBegin = false;
}
if (isNewLine) {
   isNewLine = false;
   if (c == '#' || c == '!') {
isCommentLine = true;
continue;
   }
}
if (c != '\n' && c != '\r') {
   lineBuf[len++] = c;
   if (len == lineBuf.length) {
       int newLength = lineBuf.length * 2;
       if (newLength < 0) {
           newLength = Integer.MAX_VALUE;
       }
char[] buf = new char[newLength];
System.arraycopy(lineBuf, 0, buf, 0, lineBuf.length);
lineBuf = buf;
   }
   //flip the preceding backslash flag
   if (c == '\\') {
precedingBackslash = !precedingBackslash;
   } else {
precedingBackslash = false;
   }
}
else {
   // reached EOL
   if (isCommentLine || len == 0) {
isCommentLine = false;
isNewLine = true;
skipWhiteSpace = true;
len = 0;
continue;
   }
   if (inOff >= inLimit) {
                        inLimit = (inStream==null)
                                  ?reader.read(inCharBuf)
         :inStream.read(inByteBuf);
inOff = 0;
if (inLimit <= 0) {
   return len;
}
   }
   if (precedingBackslash) {
len -= 1;
//skip the leading whitespace characters in following line
skipWhiteSpace = true;
appendedLineBegin = true;
precedingBackslash = false;
if (c == '\r') {
                            skipLF = true;
}
   } else {
return len;
   }
}
   }
}
    }
    
    /*
     * Converts encoded &#92;uxxxx to unicode chars
     * and changes special saved chars to their original forms
     */
    private String loadConvert (char[] in, int off, int len, char[] convtBuf) {
        if (convtBuf.length < len) {
            int newLen = len * 2;
            if (newLen < 0) {
       newLen = Integer.MAX_VALUE;
   } 
   convtBuf = new char[newLen];
        }
        char aChar;
        char[] out = convtBuf; 
        int outLen = 0;
        int end = off + len;

        while (off < end) {
            aChar = in[off++];
            if (aChar == '\\') {
                aChar = in[off++];   
                if(aChar == 'u') {
                    // Read the xxxx
                    int value=0;
   for (int i=0; i<4; i++) {
       aChar = in[off++];  
       switch (aChar) {
         case '0': case '1': case '2': case '3': case '4':
         case '5': case '6': case '7': case '8': case '9':
            value = (value << 4) + aChar - '0';
    break;
 case 'a': case 'b': case 'c':
                          case 'd': case 'e': case 'f':
    value = (value << 4) + 10 + aChar - 'a';
    break;
 case 'A': case 'B': case 'C':
                          case 'D': case 'E': case 'F':
    value = (value << 4) + 10 + aChar - 'A';
    break;
 default:
                              throw new IllegalArgumentException(
                                           "Malformed \\uxxxx encoding.");
                        }
                     }
                    out[outLen++] = (char)value;
                } else {
                    if (aChar == 't') aChar = '\t'; 
                    else if (aChar == 'r') aChar = '\r';
                    else if (aChar == 'n') aChar = '\n';
                    else if (aChar == 'f') aChar = '\f'; 
                    out[outLen++] = aChar;
                }
            } else {
       out[outLen++] = (char)aChar;
            }
        }
        return new String (out, 0, outLen);
    }
}

资源文件chinese_unicode_to__pinyin.txt
  评论这张
 
阅读(596)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017