返回列表 发帖

[其他] 也谈IF命令的比较顺序

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

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

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

但是具体怎么排序MSDN却没有说明(至少我没有找到),为了弄清楚默认情况下IF的比较顺序,我写了一个简单C程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
/************************************************************************/
/* Author: Demon                                                        */
/* Date: 2012/08/15                                                     */
/* Website: [url]http://demon.tw[/url]                                             */
/************************************************************************/
#define BUFFER_SIZE 1024
typedef struct _table {
    int     cp936;
    wchar_t *unicode;
    char    *name;
} table;
int compare(const void *a, const void *b);
int main()
{
    table *a;
    int n = 0, i = 0;
    wchar_t *p;
    char buf[BUFFER_SIZE], *p1, *p2;
    FILE *fp1, *fp2;
    /* 打开CP936.txt */
    fp1 = fopen("CP936.txt", "rb");
    if (fp1 == NULL) {
        fprintf(stderr, "Can't open CP936.txt\n");
        return 1;
    }
    /* 计算映射表数组的大小 */
    while (!feof(fp1) && fgets(buf, BUFFER_SIZE, fp1)) {
        if (strlen(buf) == 0 || buf[0] == '#') continue;
        if (p1 = strchr(buf, '\t')) *p1++ = '\0';
        if (p2 = strchr(p1, '\t')) *p2++ = '\0';
        while (isspace(*p1)) p1++;
        if (!*p1) continue;
        n++;
    }
    /* 创建映射表数组 */
    a = (table *) malloc(n * sizeof(table));
    /* 重新打开CP936.txt */
    fp1 = freopen("CP936.txt", "rb", fp1);
    if (fp1 == NULL) {
        fprintf(stderr, "Can't reopen CP936.txt\n");
        return 1;
    }
    /* 将数据填充到映射表数组 */
    while (!feof(fp1) && fgets(buf, BUFFER_SIZE, fp1)) {
        if (strlen(buf) == 0 || buf[0] == '#') continue;
        if (p1 = strchr(buf, '\t')) *p1++ = '\0';
        if (p2 = strchr(p1, '\t')) *p2++ = '\0';
        while (isspace(*p1)) p1++;
        if (!*p1) continue;
        p = (wchar_t *) malloc(2 * sizeof(wchar_t));
        p[0] = (wchar_t) strtol(p1, NULL, 16);
        p[1] = 0x0000;
        a[i].cp936 = strtol(buf, NULL, 16);
        a[i].unicode = p;
        a[i].name = strdup(p2);
        i++;
    }
    /* 快速排序 */
    qsort(a, n, sizeof(table), compare);
    /* 打开CP936_SORT.txt */
    fp2 = fopen("CP936_SORT.txt", "wb");
    if (fp2 == NULL) {
        fprintf(stderr, "Can't open CP936_SORT.txt\n");
        return 1;
    }
    /* 将排序后的映射表写入文件 */
    for (i = 0; i < n; i++) {
        fprintf(fp2, "0x%02X\t0x%04X\t%s", a[i].cp936, a[i].unicode[0], a[i].name);
    }
    /* 释放内存 */
    for (i = 0; i < n; i++) {
        free(a[i].unicode);
        free(a[i].name);
    }
    free(a);
    /* 关闭文件并返回 */
    fclose(fp1);
    fclose(fp2);
    return 0;
}
/* 回调函数 */
int compare(const void *a, const void *b)
{
    wchar_t *s1 = ((table *)a)->unicode;
    wchar_t *s2 = ((table *)b)->unicode;
    return lstrcmpW(s1, s2);
}COPY
CP936.TXT可以到Unicode官方网站下载到(http://unicode.org/Public/MAPPIN ... T/WINDOWS/CP936.TXT)。

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

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

不想自己编译的话可以下载我编译好的EXE:
附件: 您需要登录才可以下载或查看附件。没有帐号?注册
1

评分人数

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

TOP

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

TOP

再有个问题,带引号和不带引号内部怎么处理的?

TOP

很好!宽字符的比较就此搞定。。。

TOP

返回列表