一、原理
1. 根源
所有的字符都是一张张矢量图,然后把这些矢量图放到一起存储在字库文件里(ttf)
2. 存储
每一个字符(包括各国国家的字符,当然包括汉字)有一个对应编码的唯一内码,就是存储了。然而同一个字符(例如“中”字)不同编码对应的内码不一样,Unicode编码的话在计算机里的存储的十六进制是“2D4E”,两个字节;国标码存的是“D6D0”,两个字节;utf8存的是“E4B8AD”三个字节。
Unicode码也是一种国际标准编码,采用二个字节编码,与ASCII码不兼容。目前,在网络、Windows系统和很多大型软件中得到应用。
二、具体实现
1. 算法
汉字转拼音
字符(包括汉字)在每一种编码中都有对应的值,而汉字的数量是有限的,所以可以通过穷举法,找到汉字对应的拼音,有了拼音对应的首字母也就出来了。
目前java语言里面默认char转int采用的是unicode编码,而且unicode编码每个中文占用2个字节,utf8需要占用3个字节,本代码采用的是unicode编码去实现
声调
汉语中,能够发声的元素一共是“aeiouü”(ü在输入法中通常用v代替),发音一共有以下几种情形:
- 拼音中只有一个发音元素,则声调直接标在那个发音元素上,如:旅(lǚ)、一(yī)、个(gè)
- 拼音中出现了多个发音元素,“a”、“e”、“ou”这三种情况,声调在这个元素上,如:好(hăo)、游(yóu)、美(mĕi)、在(zài)。其它情况声调通常标在最后一个发音元素上,如:穷(qióng)
2. 实现代码
package xxx;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PinyinUtil {
private static final Map<Character, String[]> cachedPinyinMap = new HashMap<Character, String[]>();
private static final Properties unicodeProps = new Properties();
public static void main(String[] args) throws IOException {
String str = "茜绿旅,一、我世界";
for (char ch : str.toCharArray()) {
String[] pinyinArray = getPinyinArray(ch, true);
if (pinyinArray != null) {
System.out.println("------" + ch + "------");
System.out.println("首字母:" + pinyinArray[0].charAt(0));
System.out.println("首读音:" + pinyinArray[0]);
System.out.println("全部读音" + Arrays.toString(pinyinArray));
System.out.println("不带音调全部读音" + Arrays.toString(getPinyinArray(ch, false)));
}
}
}
public static String[] getPinyinArray(Character ch, boolean withTone) {
if (cachedPinyinMap.get(ch) != null) {
return format(cachedPinyinMap.get(ch), withTone);
}
String pinyin = unicodeProps.getProperty(Integer.toHexString(ch).toUpperCase());
if (pinyin != null) {
pinyin = pinyin.substring(1, pinyin.length() - 1);
cachedPinyinMap.put(ch, pinyin.split(","));
return format(cachedPinyinMap.get(ch), withTone);
}
return null;
}
private static String[] format(String[] pinyins, boolean withTone) {
String[] formatedPinyin = new String[pinyins.length];
int completeIndex = 0;
if (withTone == false) {
for (String pinyin : pinyins) {
formatedPinyin[completeIndex++] = pinyin.substring(0, pinyin.length()-1);
}
return formatedPinyin;
}
char unmarkedVowel = '/';
int indexOfUnmarkedVowel = -1;
final char charA = 'a';
final char charE = 'e';
final String ouStr = "ou";
final String allUnmarkedVowelStr = "aeiouv";
final String allMarkedVowelStr = "āáăàaēéĕèeīíĭìiōóŏòoūúŭùuǖǘǚǜü";
for (String pinyin : pinyins) {
if ((indexOfUnmarkedVowel = pinyin.indexOf(charA)) != -1
|| (indexOfUnmarkedVowel = pinyin.indexOf(charE)) != -1
|| (indexOfUnmarkedVowel = pinyin.indexOf(ouStr)) != -1) {
unmarkedVowel = pinyin.charAt(indexOfUnmarkedVowel);
} else {
for (int i = pinyin.length()-1; i > 0; i--) {
if (allUnmarkedVowelStr.indexOf(pinyin.charAt(i)) != -1) {
indexOfUnmarkedVowel = i;
unmarkedVowel = pinyin.charAt(indexOfUnmarkedVowel);
break;
}
}
}
if (indexOfUnmarkedVowel != -1) {
int index = allUnmarkedVowelStr.indexOf(unmarkedVowel);
int toneNumber = Character.getNumericValue(pinyin.charAt(pinyin.length() - 1));
char markedVowel = allMarkedVowelStr.charAt(index*5+toneNumber-1);
StringBuffer resultBuffer = new StringBuffer();
resultBuffer.append(pinyin.substring(0, indexOfUnmarkedVowel).replaceAll("v", "ü"));
resultBuffer.append(markedVowel);
resultBuffer.append(pinyin.substring(indexOfUnmarkedVowel + 1, pinyin.length() - 1).replaceAll("v", "ü"));
formatedPinyin[completeIndex++] = resultBuffer.toString();
} else {
formatedPinyin[completeIndex++] = pinyins[completeIndex];
}
}
return formatedPinyin;
}
static {
try {
init();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void init() throws IOException {
unicodeProps.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("pinyindb/unicode_to_pinyin.txt"));
}
}
3. 附件
链接: https://pan.baidu.com/s/1JPjSiRBOxllgdr-JoPMK3g 提取码: i5v3 复制这段内容后打开百度网盘手机App,操作更方便哦
三、扩展
如果喜欢用utf8编码实现解析汉子拼音的功能也是可以的,下面代码提供了将unicode编码汉字对应的拼音文件,转换成utf8编码对应的拼音文件,有兴趣的可以基于生成的附件实现汉子解析拼音功能。
@SuppressWarnings("unused")
private static void generateUTF8FileFromUnicode() throws IOException {
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("pinyindb/unicode_to_pinyin.txt");
InputStreamReader reader = new InputStreamReader(is);
BufferedReader bf = new BufferedReader(reader);
String line;
Pattern pattern = Pattern.compile("(\\S+) (\\S+)");
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("src/main/resources/pinyindb/utf8_to_pinyin.txt")));
while ( (line = bf.readLine()) != null) {
Matcher matcher = pattern.matcher(line);
if (matcher.find()) {
String utf8 = UnicodeToUTF8(matcher.group(1));
writer.write(utf8 + " " + matcher.group(2) + "\n");
System.out.println(matcher.group(1) + ":" + matcher.group(2));
}
}
bf.close();
writer.close();
}
private static String UnicodeToUTF8(String hex) throws UnsupportedEncodingException {
int i = Integer.parseInt(hex, 16);
char ch = (char)i;
byte[] bytes = new String(ch+"").getBytes("utf8");
StringBuffer sb = new StringBuffer();
for (byte b : bytes) {
sb.append(Integer.toHexString(b&0xff).toUpperCase());
}
return sb.toString();
}