Board logo

标题: [其他] 也谈IF命令的比较顺序 [打印本页]

作者: Demon    时间: 2012-8-15 16:18     标题: 也谈IF命令的比较顺序

众所周知,IF命令的排序规则既不是按照GBK编码的顺序,也不是按照Unicode编码的顺序,而是有着自己的规则,这个规则是什么呢?

在CMD内部,IF命令是调用lstrcmpW函数来比较字符串大小的(《批处理技术内幕:IF命令》),IF命令的比较规则即lstrcmpW函数的比较规则。

lstrcmp(Locale String Compare)函数的排序是与系统的语言与区域设置有关的(参考《Windows 代码页与字符顺序》)。

但是具体怎么排序MSDN却没有说明(至少我没有找到),为了弄清楚默认情况下IF的比较顺序,我写了一个简单C程序:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <windows.h>
  5. /************************************************************************/
  6. /* Author: Demon                                                        */
  7. /* Date: 2012/08/15                                                     */
  8. /* Website: [url]http://demon.tw[/url]                                             */
  9. /************************************************************************/
  10. #define BUFFER_SIZE 1024
  11. typedef struct _table {
  12.     int     cp936;
  13.     wchar_t *unicode;
  14.     char    *name;
  15. } table;
  16. int compare(const void *a, const void *b);
  17. int main()
  18. {
  19.     table *a;
  20.     int n = 0, i = 0;
  21.     wchar_t *p;
  22.     char buf[BUFFER_SIZE], *p1, *p2;
  23.     FILE *fp1, *fp2;
  24.     /* 打开CP936.txt */
  25.     fp1 = fopen("CP936.txt", "rb");
  26.     if (fp1 == NULL) {
  27.         fprintf(stderr, "Can't open CP936.txt\n");
  28.         return 1;
  29.     }
  30.     /* 计算映射表数组的大小 */
  31.     while (!feof(fp1) && fgets(buf, BUFFER_SIZE, fp1)) {
  32.         if (strlen(buf) == 0 || buf[0] == '#') continue;
  33.         if (p1 = strchr(buf, '\t')) *p1++ = '\0';
  34.         if (p2 = strchr(p1, '\t')) *p2++ = '\0';
  35.         while (isspace(*p1)) p1++;
  36.         if (!*p1) continue;
  37.         n++;
  38.     }
  39.     /* 创建映射表数组 */
  40.     a = (table *) malloc(n * sizeof(table));
  41.     /* 重新打开CP936.txt */
  42.     fp1 = freopen("CP936.txt", "rb", fp1);
  43.     if (fp1 == NULL) {
  44.         fprintf(stderr, "Can't reopen CP936.txt\n");
  45.         return 1;
  46.     }
  47.     /* 将数据填充到映射表数组 */
  48.     while (!feof(fp1) && fgets(buf, BUFFER_SIZE, fp1)) {
  49.         if (strlen(buf) == 0 || buf[0] == '#') continue;
  50.         if (p1 = strchr(buf, '\t')) *p1++ = '\0';
  51.         if (p2 = strchr(p1, '\t')) *p2++ = '\0';
  52.         while (isspace(*p1)) p1++;
  53.         if (!*p1) continue;
  54.         p = (wchar_t *) malloc(2 * sizeof(wchar_t));
  55.         p[0] = (wchar_t) strtol(p1, NULL, 16);
  56.         p[1] = 0x0000;
  57.         a[i].cp936 = strtol(buf, NULL, 16);
  58.         a[i].unicode = p;
  59.         a[i].name = strdup(p2);
  60.         i++;
  61.     }
  62.     /* 快速排序 */
  63.     qsort(a, n, sizeof(table), compare);
  64.     /* 打开CP936_SORT.txt */
  65.     fp2 = fopen("CP936_SORT.txt", "wb");
  66.     if (fp2 == NULL) {
  67.         fprintf(stderr, "Can't open CP936_SORT.txt\n");
  68.         return 1;
  69.     }
  70.     /* 将排序后的映射表写入文件 */
  71.     for (i = 0; i < n; i++) {
  72.         fprintf(fp2, "0x%02X\t0x%04X\t%s", a[i].cp936, a[i].unicode[0], a[i].name);
  73.     }
  74.     /* 释放内存 */
  75.     for (i = 0; i < n; i++) {
  76.         free(a[i].unicode);
  77.         free(a[i].name);
  78.     }
  79.     free(a);
  80.     /* 关闭文件并返回 */
  81.     fclose(fp1);
  82.     fclose(fp2);
  83.     return 0;
  84. }
  85. /* 回调函数 */
  86. int compare(const void *a, const void *b)
  87. {
  88.     wchar_t *s1 = ((table *)a)->unicode;
  89.     wchar_t *s2 = ((table *)b)->unicode;
  90.     return lstrcmpW(s1, s2);
  91. }
复制代码
CP936.TXT可以到Unicode官方网站下载到(http://unicode.org/Public/MAPPIN ... T/WINDOWS/CP936.TXT)。

程序运行后会生成CP936_SORT.txt,里面是排序后的CP936到Unicode的映射表,

第一列是GBK码,第二列是对应的Unicode代码点(Code Point),第三列是字符的Unicode名称。

不想自己编译的话可以下载我编译好的EXE:[attach]5587[/attach]
作者: plp626    时间: 2012-8-20 10:45

很好!宽字符的比较就此搞定。。。
作者: plp626    时间: 2012-8-20 10:48

再有个问题,带引号和不带引号内部怎么处理的?
作者: tiandyoin    时间: 2023-8-12 01:21

本帖最后由 tiandyoin 于 2023-8-12 01:34 编辑
  1.     默认区域设置(GB2312)排序顺序简列:  
  2.         测试命令:
  3.             if "@VTVT" leq "@FFFF "
  4. VT 为 0x0B. FF 为 0x0C
  5.         https://ss64.com/nt/sort.html
  6.             '- !"#$%()*,./:;?@[\]^_`~+<=>¬£01..89aAbBcCdDeE...zZ
  7.             '£¬' 和 ',' 的二进制码几乎相同。'£¬' 是 U+00A3,u+00AC; ',' 是 GB2312 0xA3AC。猜测官网是在搬运文本时转码出错了。
  8.         自己测得(cp936.nls?,*.nlp):
  9.            [NUL] < [空白字符] < ASCII 剩余前25个控制字符 < (0x7F) < '- < !"#$%&()*,./:;?@[\]^_`{|}~‘’“”<=>+,01..⒆⒇Ⅰ...ⅫaAbBcCdDeE...zZαβ...ЮЯ
  10.             [空白字符]顺序: SPACE,IDEOGRAPHIC SPACE,TAB,LN, , ,CR
复制代码

作者: tiandyoin    时间: 2023-8-12 01:35

  1. C 语言环境排序顺序简列 (不区分大小写):
  2.         测试命令:
  3.             TYPE "CP936.txt"  | SORT /+17 /l "C" /o "CP936_sort.txt"
  4.         https://ss64.com/nt/sort.html
  5.             !"#$%'()*+,-./01..89:;<=>?@[\]^_`aABbcCDdeE...zZ~£¬
  6.         自己测得(cp936.nls?,*.nlp):
  7.             ASCII 前32个控制字符 <  !"#$%&'()*+,-./01..89:;<=>?@[\]^_`AabBCcDdeE...yYZz{|}~ < (0x7F) < àèìòù < Α...Ωαβ...ψωАБ...ЮЯаб...яё < ‘’“” < (0x80) <Ⅰ...Ⅻⅰ...ⅹ < ①...⑩ ⑴...⒇ ⒈...⒛  < ㈠...㈩
  8.             其中同一字母不区分大小写,前后顺序是随机的,可能是 Aa 也可能是 aA.
  9.             有意思的是,这一随机不是每次运行脚本都随机,而是每次改变 "CP936.txt" 里的字母序列,
  10.             会选择多种输出方案中的一种。如果不再改变输入序列,则输出序列固定使用这一种方案。
复制代码





欢迎光临 批处理之家 (http://bathome.net./) Powered by Discuz! 7.2