`
MouseLearnJava
  • 浏览: 467408 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

中文排序

    博客分类:
  • Java
阅读更多

对于一个数组或者列表或者集合的元素进行排序是一个比较常用的需求。现有的Java类库也提供了API来实现这样的功能,比如Arrays.sort以及Collections.sort的方法。另外,我们也可以用Collator来实现中文的排序。

package demo;

import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;

public class TestCollator {

	public static void main(String[] args) {

		List<String> list = new ArrayList<String>();
		list.add("中");
		list.add("文");
		list.add("拼");
		list.add("音");
		list.add("鑫");
		list.add("犇");

		Collections.sort(list, Collator.getInstance(Locale.CHINA));

		for(String ele: list)
		{
			System.out.println(ele);
		}

	}
}


输出的结果是:







中文排序后的结果,并不完全正确。

这是因为Java使用的是Unicode编码,而常用的中文编码是GB2312,它包含了7000个字符集而且是按照拼音排序的,也是连续的。GB18030和GBK都是在此基础上扩展起来的,这样就会造成Unicode的不连续性,最终导致对有些中文字符排序不完全正确的结果。


为了能够更好地对中文进行排序,我们可以采用拼音或者笔画来对中文进行排序。本文采用了拼音来排序。将汉语转换成拼音的开源项目有PinYin4j,下载的地址如下:

http://sourceforge.net/projects/pinyin4j/files/pinyin4j-2.5.0/pinyin4j-2.5.0/pinyin4j-2.5.0.zip/download

编写汉字转换成拼音的工具类

package demo;

import java.io.UnsupportedEncodingException;
import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;

public final class PinYinUtils {

	private PinYinUtils() {
	}
	
	/**
	 * 判断一个字符是否是中文字符
	 */
	private static boolean isChineseCharacter(char c) {
		return String.valueOf(c).matches("[\\u4E00-\\u9FA5]+");
	}

	/**
	 * 将一个含有中文的字符串转换成拼音。
	 * Note: 这里只是将中文转换成拼音,其它的各种字符将保持原来的样子。
	 */
	public static String populatePinYing(String aChineseValue) {

		if (null == aChineseValue) {
			return null;
		}

		StringBuilder sb = new StringBuilder();
		char[] charArray = aChineseValue.toCharArray();

		HanyuPinyinOutputFormat outputFormat = new HanyuPinyinOutputFormat();
		outputFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
		outputFormat.setVCharType(HanyuPinyinVCharType.WITH_V);
		outputFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);

		for (int i = 0; i < charArray.length; i++) {
			if (isChineseCharacter(charArray[i])) {
				try {
					sb.append(PinyinHelper.toHanyuPinyinStringArray(
							charArray[i], outputFormat)[0]);
				} catch (BadHanyuPinyinOutputFormatCombination e) {
					e.printStackTrace();
				}
			} else {
				sb.append(charArray[i]);
			}
		}
		return sb.toString();
	}

	/**
	 * 将一个含有中文的字符串转换成拼音。
	 * Note: 这里只是将中文转换成拼音,其它的各种字符将保持原来的样子。
	 * 
	 * 在這裡多了一個參數needToCorrectSpelling,这个主要用于调整姓氏的发音。
	 * 如 '单', 这个字作为姓氏念 shan, 但同时也有dan的发音等。
	 * 
	 * 为了保证姓氏发音的正确性,将了这个参数和相关的简单逻辑
	 */
	public static String populatePinYing(String aChineseValue,
			boolean needToCorrectSpelling) {

		if (null == aChineseValue) {
			return null;
		}

		StringBuilder sb = new StringBuilder();
		char[] charArray = aChineseValue.toCharArray();

		HanyuPinyinOutputFormat outputFormat = new HanyuPinyinOutputFormat();
		outputFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
		outputFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
		outputFormat.setVCharType(HanyuPinyinVCharType.WITH_V);

		String surname = null;
		for (int i = 0; i < charArray.length; i++) {
			if (isChineseCharacter(charArray[i])) {
				try {
					if (needToCorrectSpelling && 0 == i) {
						surname = SurnameDictionary
								.populateCorrectSpelling(charArray[i]);
						if (null == surname) {
							sb.append(PinyinHelper.toHanyuPinyinStringArray(
									charArray[i], outputFormat)[0]);
						} else {
							sb.append(surname);
						}
					} else {
						sb.append(PinyinHelper.toHanyuPinyinStringArray(
								charArray[i], outputFormat)[0]);
					}

				} catch (BadHanyuPinyinOutputFormatCombination e) {
					e.printStackTrace();
				}
			} else {
				sb.append(charArray[i]);
			}
		}
		return sb.toString();
	}

	public static void main(String[] args) throws UnsupportedEncodingException {
		String x = "拼音4j%^**Cool12568 カ キ ";
		System.out.println(populatePinYing(x));
	}
}



本文的一个简单例子是对名字进行排序,所以下面创建两个类 User 和 NameComparator
package demo;

import java.io.Serializable;

public class User implements Serializable {

	private static final long serialVersionUID = -1221268239932915488L;

	private String name;

	private int age;

	/**
	 * @param name
	 * @param age
	 */
	public User(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public User() {

	}

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name
	 *            the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * @return the age
	 */
	public int getAge() {
		return age;
	}

	/**
	 * @param age
	 *            the age to set
	 */
	public void setAge(int age) {
		this.age = age;
	}

}


package demo;

import java.util.Comparator;

public class NameComparator implements Comparator<User> {

	public int compare(User u1, User u2) {

		String name1 = u1.getName();
		String name2 = u2.getName();

		return PinYinUtils.populatePinYing(name1).compareTo(
				PinYinUtils.populatePinYing(name2));
	}

}


测试代码如下:

package demo;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Test {

	public static void main(String[] args) {

		run();
	}

	private static void run() {
		List<User> list = prepareTestUserList();

		printListBeforeSorting(list);

		Collections.sort(list, new NameComparator());

		printListAfteSorting(list);
	}

	private static void printListAfteSorting(List<User> list) {
		System.out.println("After sorting.....");
		printList(list);
	}

	private static void printListBeforeSorting(List<User> list) {
		System.out.println("Before sorting.....");
		printList(list);
	}

	private static void printList(List<User> list) {
		for (User user : list) {
			System.out.println(user.getName());
		}
	}

	private static List<User> prepareTestUserList() {
		List<User> list = new ArrayList<User>();
		User u = new User();
		u.setName("张三");
		u.setAge(21);
		list.add(u);

		u = new User();
		u.setName("李四");
		u.setAge(18);
		list.add(u);

		u = new User();
		u.setName("王五");
		u.setAge(25);
		list.add(u);

		u = new User();
		u.setName("鑫鑫");
		u.setAge(89);
		list.add(u);

		u = new User();
		u.setName("范范");
		u.setAge(89);
		list.add(u);

		u = new User();
		u.setName("单一号");
		u.setAge(89);
		list.add(u);

		u = new User();
		u.setName("犇犇");
		u.setAge(89);
		list.add(u);

		return list;
	}

}


Before sorting.....
张三
李四
王五
鑫鑫
范范
单一号
犇犇
After sorting.....
犇犇
单一号
范范
李四
王五
鑫鑫
张三

从上述的排序结果,可以看出“单一号”并没有在一个准确的位置上。产生这种情况是因为中文存在多音字,而且姓氏中的发音会不一样。如“单”在姓氏中需要发“shan”,用PinYin4j能够返回多音字,因为不知道哪一个正确,我在代码中返回了第一个 “dan”。
为了能够保证姓氏发声的正确性,个人想到的有三种:1. 数据库中存储姓氏发音的表
2. 将姓氏的发音存储在一个key-value的properties文件中
3. 创建一个姓氏发音的字典类

本文简单采用第三种方式来实现:


package demo;


public class SurnameDictionary {

	public static String populateCorrectSpelling(char surname) {

		//TODO:
		if ('单' == surname) {
			return "shan";
		}

		return null;
	}
	
}



比较器中加入参数,将SurnameDictionary运用上去。

package demo;

import java.util.Comparator;

public class NameComparator implements Comparator<User> {

	public int compare(User u1, User u2) {

		String name1 = u1.getName();
		String name2 = u2.getName();

		return PinYinUtils.populatePinYing(name1, true).compareTo(
				PinYinUtils.populatePinYing(name2, true));
	}

}


重新运行结果,我们将得到正确的名字排序结果:

Before sorting.....
张三
李四
王五
鑫鑫
范范
单一号
犇犇
After sorting.....
犇犇
范范
李四
单一号
王五
鑫鑫
张三

3
2
分享到:
评论
4 楼 cb_0312 2016-06-22  
SurnameDictionary文章我没看完,现在懂了
3 楼 cb_0312 2016-06-22  
SurnameDictionary.populateCorrectSpelling(charArray[i])

这个类是什么?我怎么找不到相关信息?
2 楼 MouseLearnJava 2013-07-17  
oaklet 写道
只能做到这一步了,再向下没法做了,毕竟有些怪字音不确定,非人力所能为,例如
常见的姓氏用字异读有:
  重:Chóng 音崇;
  区:ōu 音欧;
  仇:Qiú 音求;
  秘:Bì 音闭;
  冼:Xiǎn 音显;
  解:Xiè 音谢;
  折:Shè 音舌;
  单:Shàn 音善;
  朴:Piáo 音瓢;
  翟:Zhá 音宅;
  查:Zhā 音渣;
  万俟:Mò qí 音莫奇;
  尉迟:Yù chí 音玉迟;等等。
  还有一些姓氏用字,一字两音,同一个作为姓氏的汉字,由于有两个读音,就代表了两个不同的姓,而且不一源流。如:“乐”姓,北方读音与“月”字同,而南方读音则与“勒”字同音;“召”姓,汉族人读者作“少”,而傣族人则读作“赵”;原籍在中原一带的“罩”姓,读音作“谈”,而注籍两广或壮族的则读音与“秦”字同。这些同字不同音者,分别表示不同的姓,不能认为是同一姓氏在不同地区、不同族属的不同读法。


非常感谢你的评论。 姓氏用字异读方面,个人觉得能够通过建立一个发音词典库等方式加以改进。像姓氏一字多读的情况,发什么音不确定性太大,不好做。

在中文排序(如名字)上,尽可能考虑到姓氏异读的情况,对姓氏一字多音的情况保持一颗宽容的心吧。

其他博友有好的建议或意见,也请一起分享吧
1 楼 oaklet 2013-07-16  
只能做到这一步了,再向下没法做了,毕竟有些怪字音不确定,非人力所能为,例如
常见的姓氏用字异读有:
  重:Chóng 音崇;
  区:ōu 音欧;
  仇:Qiú 音求;
  秘:Bì 音闭;
  冼:Xiǎn 音显;
  解:Xiè 音谢;
  折:Shè 音舌;
  单:Shàn 音善;
  朴:Piáo 音瓢;
  翟:Zhá 音宅;
  查:Zhā 音渣;
  万俟:Mò qí 音莫奇;
  尉迟:Yù chí 音玉迟;等等。
  还有一些姓氏用字,一字两音,同一个作为姓氏的汉字,由于有两个读音,就代表了两个不同的姓,而且不一源流。如:“乐”姓,北方读音与“月”字同,而南方读音则与“勒”字同音;“召”姓,汉族人读者作“少”,而傣族人则读作“赵”;原籍在中原一带的“罩”姓,读音作“谈”,而注籍两广或壮族的则读音与“秦”字同。这些同字不同音者,分别表示不同的姓,不能认为是同一姓氏在不同地区、不同族属的不同读法。

相关推荐

    MFC汉字排序

    在Microsoft Foundation Classes (MFC)库中,进行汉字排序是一个常见的需求,特别是在处理中文数据时。MFC提供了丰富的类和函数来支持C++编程,它为Windows应用程序开发提供了一种结构化的方式。在这个主题中,我们...

    解决Sqlite中文排序问题(网页)

    在SQLiteDatabase,特别是SQLite3中,处理中文排序问题是一个常见的挑战。SQLite3是一个轻量级、自包含的数据库引擎,广泛应用于嵌入式系统和移动应用。然而,它默认的排序规则可能不适用于中文字符,因为中文字符的...

    java中文排序,数字字母汉字排序

    默认情况下,Java使用自然排序,即按照字符串的Unicode值进行排序,这对于英文字符和数字来说通常是合适的,但对于中文字符则不是我们期望的排序方式。 为了实现中文、数字和字母的排序,我们需要创建一个`...

    EasyUI Datagrid 中文排序的问题

    本文将详细介绍如何解决EasyUI Datagrid中的中文排序问题,并分别从前端和后端两个角度给出具体的实现方案。 #### 二、EasyUI Datagrid简介 EasyUI 是一个基于 jQuery 的简单而强大的 UI 库,它提供了大量的用户...

    数字字母中文排序

    ### 数字字母中文排序知识点详解 #### 一、概述 在处理包含数字、字母与中文字符的数据时,正确的排序逻辑尤为重要。本篇文章将基于提供的代码片段来深入探讨如何实现一个能够处理这三种类型字符的排序算法,并且...

    SQL 中文排序

    在处理数据库中的中文数据时,我们常常会遇到中文排序的问题。默认情况下,SQL的排序机制可能会导致中文字符按照其内部编码(通常是Unicode编码)进行排序,这与我们的预期(比如按照拼音或笔画顺序)可能不一致。...

    Ext中文排序问题

    然而,默认情况下,ExtJS的数据排序功能主要基于英文字符集,当遇到中文数据时,排序结果可能不符合预期。 #### 问题分析 中文排序的核心在于正确处理中文字符的比较。由于中文字符的Unicode编码并非按照汉语拼音或...

    微信小程序(汉字排序+关键字搜索+页面跳转)

    小程序案例视频,汉字排序

    sortProject(Qt5中文排序与英文排序实现).zip

    在本文中,我们将深入探讨如何使用Qt5框架在VS2015开发环境中实现中文和英文的排序功能。Qt5提供了QCollator和QLocale库,使得开发者能够方便地处理各种语言的排序需求,包括中文的拼音排序和笔画排序。 首先,...

    iOS 中文排序所需头文件

    在iOS开发中,当涉及到中文数据的排序时,由于中文字符的复杂性,不能像英文那样简单地按照字典顺序进行排序。此时,我们需要引入特定的算法和库来处理中文字符的拼音转换,以便实现正确的排序。这篇内容将详细介绍...

    oracle数据库中汉字排序方法

    ### Oracle数据库中汉字排序方法 在Oracle数据库中处理中文数据时,经常需要对包含中文字符的数据表...通过以上内容的学习与实践,我们可以有效地在Oracle数据库中实现汉字排序功能,从而更好地管理和利用中文数据。

    Extjs grid 中文排序问题修正

    EXTJS Grid默认的排序机制对于英文字符处理得较好,但对于中文字符,由于编码和比较规则的不同,可能会导致排序结果不正确。本教程将详细讲解如何修正EXTJS Grid中的中文排序问题。 首先,我们需要理解EXTJS Grid的...

    解决Hibernate中MySQL的中文排序

    本文将深入探讨如何解决这些问题,确保MySQL数据库中的中文数据能按照正确的顺序进行排序。 首先,我们需要了解MySQL字符集的基础知识。MySQL支持多种字符集,如GBK、UTF-8等,它们用于存储和表示不同语言的字符。...

    swift-中文排序

    Swift本身提供的排序功能对于英文字符集是足够的,但当涉及到中文字符时,由于Unicode编码的复杂性,直接使用默认的排序方法可能无法得到预期的结果。中文字符的排序需要考虑到汉字的拼音、笔画或者字典顺序。...

    mysql如何根据汉字首字母排序

    在线中英文根据首字母排序工具: http://tools.jb51.net/aideddesign/zh_paixu 您可能感兴趣的文章:mysql的中文数据按拼音排序的2个方法mysql如何按照中文排序解决方案MySQL按照汉字的拼音排序简单实例

    关于中文英文混合排序javaDemo

    在Java编程语言中,处理中文和英文混合的排序问题是一个常见的需求,特别是在处理用户输入、数据库数据或文件名等场景。这个"关于中文英文混合排序javaDemo"的示例主要展示了如何实现这样的功能。让我们深入探讨一下...

    EXT支持GRID中文排序

    中文排序与英文或其他语言排序的主要区别在于,中文字符通常按照拼音顺序而非字母顺序进行排列。因此,直接使用JavaScript内置的`localeCompare`函数可能无法达到预期的效果。此外,还需要考虑到中文字符的特殊性...

    解决Ext Grid中文排序问题

    在中文环境中,我们通常希望按照汉字的拼音或笔画顺序来排序。 为了解决这个问题,我们可以重写Ext.data.Store的`applySort`函数,以便在进行本地排序时应用中文排序规则。上述代码正是这样一个解决方案。它将原生...

    js中文排序

    在JavaScript中,中文排序是一个常见的需求,特别是在处理包含中文数据的数组时。`sort()`函数是JavaScript中的一个内置方法,用于对数组进行原地排序。然而,它默认的排序方式是基于Unicode编码,这并不适合中文...

Global site tag (gtag.js) - Google Analytics