对于包含汉字的字符串来说,排序的方式主要有两种:一种是拼音,一种是笔画。

本文就讲述如何实现按笔划排序的比较器(Comparator)。


作者:Jeff 发表于:2007年12月21日 11:27 最后更新于: 2007年12月21日 12:38 
版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明
http://www.blogjava.net/jeff-lau/archive/2007/12/21/169257.html

笔画排序

要按笔画排序,就要实现笔画比较器。

class StokeComparator implements Comparator<String>

如果有个方法可以求得汉字的笔画数,上面的功能就很容易实现。如何求一个汉字的笔画数?最容易想到的就是查表法。建一个汉字笔画数表,如:

汉字 Unicode编码 笔画数
U4E00 1
U4E8C 2
U9F8D 16
... ... ...

表二

如果是连续的、按unicode编码排好顺序的表,实际存储在笔画数表中的只需最后一列就够了。

那如何建这个表呢?这个表存储在哪里?

建汉字笔画数表

现在大多数系统还只能支持Unicode中的基本汉字那部分汉字,编码从U9FA6-U9FBF。所以我们只建这部分汉字的笔画表。汉字笔画数表,我们可以按照下面的方法生成:

  1. 用java程序生成一个文本文件(Chinese.csv)。包括所有的从U9FA6-U9FBF的字符的编码和文字。利用excel的按笔画排序功能,对Chinese.csv文件中的内容排序。
  2. 编写Java程序分析Chinese.csv文件,求得笔画数, 生成ChineseStroke.csv。矫正笔画数,重新按汉字的Unicode编码对ChineseStroke.csv文件排序。
  3. 只保留ChineseStroke.csv文件的最后一列,生成Stroke.csv。

在这里 下载上面3个步骤生成的3个文件

生成Chinese.csv的Java程序

  1. /**
  2. * @author Jeff
  3. *
  4. * Copyright (c) 复制或转载本文,请保留该注释。
  5. */
  6. package chinese.utility.preface;
  7.  
  8. import java.io.IOException;
  9. import java.io.PrintWriter;
  10.  
  11. public class ChineseCoder {
  12.  
  13. public static void main(String[] args) throws IOException {
  14. PrintWriter out = new PrintWriter("Chinese.csv");
  15. // 基本汉字
  16. for (char c = 0x4E00; c <= 0x9FA5; c++) {
  17. out.println((int) c + "," + c);
  18. }
  19. out.flush();
  20. out.close();
  21.  
  22. }
  23.  
  24. }

初始化笔画数

从Excel排序过后的Chinese.csv文件来看,排好序的文件还是有一定规律的。在文件的第9行-12行可以看出:逐行扫描的时候,当unicode会变小了,笔画数也就加1。

20059,乛
20101,亅
19969,丁
19970,丂

用下面的Java程序分析吧。

  1. /**
  2. * @author Jeff
  3. *
  4. * Copyright (c) 复制或转载本文,请保留该注释。
  5. */
  6. package chinese.utility.preface;
  7.  
  8. import java.io.File;
  9. import java.io.IOException;
  10. import java.io.PrintWriter;
  11. import java.util.Scanner;
  12.  
  13. public class Stroke {
  14.  
  15. /**
  16. * @param args
  17. * @throws IOException
  18. */
  19. public static void main(String[] args) throws IOException {
  20. Scanner in = new Scanner(new File("Chinese.csv"));
  21. PrintWriter out = new PrintWriter("ChineseStroke.csv");
  22. String oldLine = "";
  23. int stroke = ;
  24. while (in.hasNextLine()) {
  25. String line = in.nextLine();
  26. if (line.compareTo(oldLine) < ) {
  27. stroke++;
  28. }
  29. oldLine = line;
  30. out.println(line + "," + stroke);
  31. }
  32. out.flush();
  33. out.close();
  34. in.close();
  35. }
  36.  
  37. }

上面用的这个规律有问题吗?有问题,从ChineseStroke.csv文件抽取最后几个汉字就发现,笔画数不对。为什么呢?

  • 笔画数可能不是连续的。
  • n+1笔画数的最小Unicode码可能比n笔画数的最大Unicode码要大

我们要人工核对ChineseStroke文件,但只要核对在笔画变化的那几个汉字的笔画数。最后,我发现,只有笔画数多于30的少数几个汉字的笔画数不对。核对并矫正笔画数后,用Excel按Unicode重新排序,去掉汉字和Unicode两列,只保留笔画数那列,得到Stroke.csv文件。

求得笔画数的方法和笔画比较器方法

求得笔画数的方法测试代码:

  1. /**
  2. * @author Jeff
  3. *
  4. * Copyright (c) 复制或转载本文,请保留该注释。
  5. */
  6. package chinese.utility.test;
  7.  
  8. import static org.junit.Assert.assertEquals;
  9.  
  10. import org.junit.Before;
  11. import org.junit.Test;
  12. import chinese.utility.Chinese;
  13.  
  14. public class StrokeTest {
  15.  
  16. Chinese chinese;
  17.  
  18. @Before
  19. public void setUp() {
  20. chinese = new Chinese();
  21. }
  22.  
  23. @Test
  24. public void testStroke() {
  25. assertEquals(, chinese.stroke('一'));
  26. }
  27.  
  28. @Test
  29. public void testStroke2() {
  30. assertEquals(, chinese.stroke('二'));
  31. }
  32.  
  33. @Test
  34. public void testStroke16() {
  35. assertEquals(, chinese.stroke('龍'));
  36. }
  37.  
  38. @Test
  39. public void testStrokeABC() {
  40. assertEquals(-, chinese.stroke('a'));
  41. }
  42.  
  43. }

求得笔画数的方法代码

  1. /**
  2. * @author Jeff
  3. *
  4. * Copyright (c) 复制或转载本文,请保留该注释。
  5. */
  6. package chinese.utility;
  7.  
  8. import java.util.Comparator;
  9.  
  10. public class StrokeComparator implements Comparator<String> {
  11.  
  12. public int compare(String o1, String o2) {
  13.  
  14. Chinese chinese = new Chinese();
  15.  
  16. for (int i = ; i < o1.length() && i < o2.length(); i++) {
  17. int codePoint1 = o1.codePointAt(i);
  18. int codePoint2 = o2.codePointAt(i);
  19. if (codePoint1 == codePoint2)
  20. continue;
  21.  
  22. int stroke1 = chinese.stroke(codePoint1);
  23. int stroke2 = chinese.stroke(codePoint2);
  24.  
  25. if (stroke1 < || stroke2 < ) {
  26. return codePoint1 - codePoint2;
  27. }
  28.  
  29. if (stroke1 != stroke2) {
  30. return stroke1 - stroke2;
  31. }
  32. }
  33.  
  34. return o1.length() - o2.length();
  35. }
  36. }

笔画比较器测试

  1. /**
  2. * @author Jeff
  3. *
  4. * Copyright (c) 复制或转载本文,请保留该注释。
  5. */
  6. package chinese.utility.test;
  7.  
  8. import java.util.Comparator;
  9.  
  10. import org.junit.Assert;
  11. import org.junit.Before;
  12. import org.junit.Test;
  13.  
  14. import chinese.utility.StrokeComparator;
  15.  
  16. public class StrokeComparatorTest {
  17.  
  18. private Comparator<String> comparator;
  19.  
  20. @Before
  21. public void setUp() {
  22. comparator = new StrokeComparator();
  23. }
  24.  
  25. /**
  26. * 相同笔画数
  27. */
  28. @Test
  29. public void testCompareEquals() {
  30. Assert.assertTrue(comparator.compare("一", "丨") == );
  31. }
  32.  
  33. /**
  34. * 不同笔画数
  35. */
  36. @Test
  37. public void testCompare() {
  38. Assert.assertTrue(comparator.compare("一", "二") < );
  39. Assert.assertTrue(comparator.compare("唔", "马") > );
  40. }
  41.  
  42. /**
  43. * 长度不同
  44. */
  45. @Test
  46. public void testCompareDefficultLength() {
  47. Assert.assertTrue(comparator.compare("二", "二一") < );
  48. }
  49.  
  50. /**
  51. * 非汉字的比较
  52. */
  53. @Test
  54. public void testABC() {
  55. Assert.assertTrue(comparator.compare("一", "a") > );
  56. Assert.assertTrue(comparator.compare("a", "b") < );
  57. }
  58. }

笔画比较器

  1. /**
  2. * @author Jeff
  3. *
  4. * Copyright (c) 复制或转载本文,请保留该注释。
  5. */
  6. package chinese.utility.test;
  7.  
  8. import java.util.Comparator;
  9.  
  10. import org.junit.Assert;
  11. import org.junit.Before;
  12. import org.junit.Test;
  13.  
  14. import chinese.utility.StrokeComparator;
  15.  
  16. public class StrokeComparatorTest {
  17.  
  18. private Comparator<String> comparator;
  19.  
  20. @Before
  21. public void setUp() {
  22. comparator = new StrokeComparator();
  23. }
  24.  
  25. /**
  26. * 相同笔画数
  27. */
  28. @Test
  29. public void testCompareEquals() {
  30. Assert.assertTrue(comparator.compare("一", "丨") == );
  31. }
  32.  
  33. /**
  34. * 不同笔画数
  35. */
  36. @Test
  37. public void testCompare() {
  38. Assert.assertTrue(comparator.compare("一", "二") < );
  39. Assert.assertTrue(comparator.compare("唔", "马") > );
  40. }
  41.  
  42. /**
  43. * 长度不同
  44. */
  45. @Test
  46. public void testCompareDefficultLength() {
  47. Assert.assertTrue(comparator.compare("二", "二一") < );
  48. }
  49.  
  50. /**
  51. * 非汉字的比较
  52. */
  53. @Test
  54. public void testABC() {
  55. Assert.assertTrue(comparator.compare("一", "a") > );
  56. Assert.assertTrue(comparator.compare("a", "b") < );
  57. }
  58. }

其他程序的汉字排序

  Microsoft在这方面做得比较好。如Sql server 2000,Word和Excel都能按拼音和笔画排序。而Oracle只能是采取宽松拼音排序法。

Java汉字排序(3)按笔划排序的更多相关文章

  1. Java汉字排序(2)按拼音排序

    对于包含汉字的字符串来说,排序的方式主要有两种:一种是拼音,一种是笔画. 本文就讲述如何实现按拼音排序的比较器(Comparator). 作者:Jeff 发表于:2007年12月21日 11:27 最 ...

  2. Java汉字排序(1)排序前要了解的知识(数组和list的排序接口)

    对于包含汉字的字符串来说,排序的方式主要有两种:一种是拼音,一种是笔画. 本文就讲述如何实现按拼音排序的比较器(Comparator). 作者:Jeff 发表于:2007年12月21日 11:27 最 ...

  3. Java面试宝典系列之基础排序算法

    本文就是介绍一些常见的排序算法.排序是一个非常常见的应用场景,很多时候,我们需要根据自己需要排序的数据类型,来自定义排序算法,但是,在这里,我们只介绍这些基础排序算法,包括:插入排序.选择排序.冒泡排 ...

  4. Java常见排序算法之Shell排序

    在学习算法的过程中,我们难免会接触很多和排序相关的算法.总而言之,对于任何编程人员来说,基本的排序算法是必须要掌握的. 从今天开始,我们将要进行基本的排序算法的讲解.Are you ready?Let ...

  5. Java比较器对数组,集合排序一

    数组排序非常简单,有前辈们的各种排序算法,再加上Java中强大的数组辅助类Arrays与集合辅助类Collections,使得排序变得非常简单,如果说结合比较器Comparator接口和Collato ...

  6. java结构与算法之选择排序

    一 .java结构与算法之选择排序(冒择路兮快归堆) 什么事选择排序:从一组无序数据中选择出中小的的值,将该值与无序区的最左边的的值进行交换. 简单的解释:假设有这样一组数据 12,4,23,5,找到 ...

  7. 使用C语言和Java分别实现冒泡排序和选择排序

    经典排序算法--冒泡和选择排序法 Java实现冒泡排序 基本思想是,对相邻的元素进行两两比较,顺序相反则进行交换,这样,每一趟会将最小或最大的元素放到顶端,最终达到完全有序,首先看个动图: 我们要清楚 ...

  8. 【java多线程系列】java内存模型与指令重排序

    在多线程编程中,需要处理两个最核心的问题,线程之间如何通信及线程之间如何同步,线程之间通信指的是线程之间通过何种机制交换信息,同步指的是如何控制不同线程之间操作发生的相对顺序.很多读者可能会说这还不简 ...

  9. 我们一起来排序——使用Java语言优雅地实现常用排序算法

    破阵子·春景 燕子来时新社,梨花落后清明. 池上碧苔三四点,叶底黄鹂一两声.日长飞絮轻. 巧笑同桌伙伴,上学径里逢迎. 疑怪昨宵春梦好,元是今朝Offer拿.笑从双脸生. 排序算法--最基础的算法,互 ...

随机推荐

  1. HTML5 内联框架iFrame

    由于现在frame和frameset很少使用,已经过时了,已经被div+CSS代替了,所以,这里只是举例说明一下,当下还在使用的内联框架iFrame 所谓的iFrame内联框架,我的理解就是在网页内部 ...

  2. AngularJS(7)-表格

    ng-repeat 指令可以完美的显示表格. <!DOCTYPE html> <html lang="en"> <head> <meta ...

  3. ZENCART 打开/关闭日志文件

    优秀的php开源程序很多都只带生成日志文件的功能,这类功能的开发可以帮助到站长在调试网站的时候及时的改正网站存在的错误,但是这类错误日志由来并非网站出现什么严重不可挽救的错误,大部分是一些未定义变量这 ...

  4. [Oracle]Oracle学习小结(1)

    1.查看Oracle数据库中的所有用户: (1)使用具有DBA权限的账户登录数据库: (2)执行select username from dba_users. SQL> conn sys 输入口 ...

  5. php计算代码运行时间与内存使用的一段代码

    计算运行时间及内存使用,代码如下: <?php //开始计时 $HeaderTime = microtime(true);//参数true表示返回浮点数值 //代码 //... printf(& ...

  6. PHP的$_SERVER['HTTP_HOST']获取服务器地址功能详解,$_SERVER['HTTP_X_FORWARDED_HOST']

    uchome的index文件中的二级域名功能判断,使用了php的$_SERVER['HTTP_HOST'],开始对这个不是很了解,所以百度了一下,发现一篇帖子有点意思,转发过来做个记录. 在php中, ...

  7. 统计网卡TX(发送)RX(接受)流量脚本

    显示网卡流量的方法蛮多,一般我们可以通过dstat来查看,但dstat不一定所有的机器都有安装.而我们知道,通过ifconfig可以看到某一网卡发送与接收的字节数,所以我们可以写一个脚本来统计一下. ...

  8. linux tail

    tail 命令从指定点开始将文件写到标准输出,使用tail命令的-f选项可以方便的查阅正在改变的日志文件,tail -f filename会把filename里最尾部的内容显示在屏幕上,并且不但刷新, ...

  9. linux驱动系列之arm汇编

    在arm平台学习linux时,会遇到arm汇编指令,arm汇编指令与8086汇编指令很多地方都不同,在此记下来以免后面忘了,同时在学习了汇编指令之后分析一些汇编指令编写的代码. 一.相对跳转指令b.b ...

  10. CSS定义字体间距 字体行与行间距

    CSS定义字体行间距 line-height:xxpx; CSS定义字体间距 下面我们讲述一下CSS的文本属性,还是先来看一下文本属性的详细列表: 属性     属性含义     属性值 Word-s ...