仿照Windows文件排序思路进行文件排序

起因,某天某个产品提了某个需求指派给一个倒霉蛋:

在这里插入图片描述
   怕我看不明白还特地把百度到的网址给我放上边了:知乎地址
   看完了之后我哭了,这特么也太复杂了!(好想给产品一个卡普的爱的铁拳),任务都已经派下来只好慢慢做了,下面记录一下开发过程。

   目标很明确,我们只需要实现一个两个字符串比较的函数:

1.开发之前前首先要明白QString怎么存储字符串的:

  • QString 类存储的字符串默认是Unicode编码格式
  • 来自 char* 的数据 - 默认被当作UTF-8编码格式

2.确定比较规则:

   参考知乎文档整理排序规则:(优先级递减)

核心规则:

  1. 文件夹 < 文件:文件夹排在文件之前
  2. 空字符串 < 非空字符串
  3. 忽略名称中的前导字符(空格(中/英)或 ’ 或 - 或 ——),自左向右依次比较所有字符,字符相同按前导(’ < - < ——)

字符具体排序规则:

  1. 先比较类型:符号 < 数字 < 英文字母 < 汉字
  2. 类型相同,按下列规则排序:
  • 符号 :固定顺序(查表)
  • 数字:直接比较数值
  • 英文字母:按a-z的顺序排列(先不区分大小写比较,相同则遵循大写在前原则,实际Windows文件名好像不区分大小写)
  • 汉字:转拼音后按英文字母排序

3.编码思路

	//比较字符串
	CompareResult compareQString(const QString& name1, const QString& name2);
  1. 区分文件和文件夹类型,类型不同直接出结果
  2. 类型相同情况下判断是否为空字符串,name1和name2含有空字符串直接出结果
  3. 都不非空则循环取一个合法字符(非先导字符),然后比较字符,比较字符时如果得出结果不相等则直接出结果,相等则需要根据先导字符确定结果
    // 比较字符
    CompareResult compareChar(const QChar& char1, const QChar& char2);
  1. 先判断字符类型,不相同直接出结果
  2. 类型相同则根据内容比较大小(不同类型规则不同)
  • 符号 :固定顺序(查表)
  • 数字:直接比较数值
  • 英文字母:按a-z的顺序排列(先不区分大小写比较,相同则遵循大写在前原则,实际Windows文件名好像不区分大小写)
  • 汉字:转拼音后按英文字母排序
  • 未知类型直接按Unicode数值排序

4.代码示例

下面只是提供一个思路,目前已经能解决我这个任务了,后续有机会完善的话会重构一下代码写一个小demo出来(想不出怎么用一个精巧的方式写一个排序算法,后续代码功底变强了可能会写的更好吧)

// 准备工作1.定义两个枚举类型
	
	// 比较结果
    enum CompareResult
    {
        LessThan = 0,   //小于
		GreaterThan,    //大于
        Equality        //相等
    }; 
    //字符类型
   	enum CharType
	{
        Symbol = 0,         //符号映射表中符号
        Digit,              //数字
        Letter,             //字母
        ChineseCharacters,  //汉字
        UnKonw              //未知类型
	};

// 准备工作2.建立符号顺序表和汉字拼音映射表,拼音文件网上找的

    QHash<QChar, quint32>    m_symbolTable;  //标点符号映射表(value决定顺序)
    QHash<QChar, QString>    m_pinyinTable;  //汉字拼音映射表
    
	bool tableInit()
	{
	    //符号表初始化
	    m_symbolTable.insert(QString(" ").at(0), 0);
		m_symbolTable.insert(QString("!").at(0), 1);
		m_symbolTable.insert(QString("!").at(0), 2);
		m_symbolTable.insert(QString("#").at(0), 3);
		m_symbolTable.insert(QString("$").at(0), 4);
		m_symbolTable.insert(QString("%").at(0), 5);
		m_symbolTable.insert(QString("&").at(0), 6);
		m_symbolTable.insert(QString("(").at(0), 7);
		m_symbolTable.insert(QString("(").at(0), 8);
		m_symbolTable.insert(QString(")").at(0), 9);
		m_symbolTable.insert(QString(")").at(0), 10);
		m_symbolTable.insert(QString(",").at(0), 11);
		m_symbolTable.insert(QString(",").at(0), 12);
		m_symbolTable.insert(QString("、").at(0), 13);
		m_symbolTable.insert(QString(".").at(0), 14);
		m_symbolTable.insert(QString("。").at(0), 15);
		m_symbolTable.insert(QString(";").at(0), 16);
		m_symbolTable.insert(QString(";").at(0), 17);
		m_symbolTable.insert(QString("?").at(0), 18);
		m_symbolTable.insert(QString("@").at(0), 19);
		m_symbolTable.insert(QString("[").at(0), 20);
		m_symbolTable.insert(QString("]").at(0), 21);
		m_symbolTable.insert(QString("^").at(0), 22);
		m_symbolTable.insert(QString("_").at(0), 23);
		m_symbolTable.insert(QString("`").at(0), 24);
		m_symbolTable.insert(QString("{").at(0), 25);
		m_symbolTable.insert(QString("}").at(0), 26);
		m_symbolTable.insert(QString("~").at(0), 27);
		m_symbolTable.insert(QString("’").at(0), 28);
		m_symbolTable.insert(QString("“").at(0), 29);
		m_symbolTable.insert(QString("”").at(0), 30);
		m_symbolTable.insert(QString("《").at(0), 31);
		m_symbolTable.insert(QString("》").at(0), 32);
		m_symbolTable.insert(QString("¥").at(0), 33);
		m_symbolTable.insert(QString("【").at(0), 34);
		m_symbolTable.insert(QString("】").at(0), 35);
		m_symbolTable.insert(QString("+").at(0), 36);
		m_symbolTable.insert(QString("=").at(0), 37);
		m_symbolTable.insert(QString("·").at(0), 38);
		m_symbolTable.insert(QString("…").at(0), 39);
	    //汉字拼音映射初始化
		//1.从配置文件读取拼音数组
		QFile file(":/resource/PinyinTable/PinyinTable.txt");
	    QStringList pinyinList;
		if (file.open(QFile::ReadOnly | QFile::Text)) {
			QString str = file.readAll();
	        pinyinList = str.split(" ");
		}
	    if (pinyinList.size()!=(0x9FA5 - 0x4E00 + 1))
	    {
	        return false;
	    }
	    //2.建立映射
	    for (int i = 0x4E00;i<= 0x9FA5;i++)
	    {
	        //拼音后加上索引位置(确保后续同音字也能比较大小)
	        m_pinyinTable.insert(QChar(i), pinyinList.at(i - 0x4E00) + QString::number(i));
	    }
	}
	
// 编码
	// 判断字符是不是前导字符
	int isPlaceholder(const QChar& _char)
	{
	    if (_char == QChar('’'))
	    {
	        return 1;
	    }
		if (_char == QChar('-'))
		{
			return 2;
		}
		if (_char == QChar('—'))
		{
			return 3;
		}
	    return -1;
	}
	// 获取字符类型
	CharType getCharType(const QChar& _char)
	{
	    if (m_symbolTable.contains(_char))
	    {
	        return Symbol;
	    }
	    if (_char.isDigit())
	    {
	        return Digit;
	    }
	    if (_char.isLetter())
	    {
	        return Letter;
	    }
	    if (_char.unicode() <= 0x9FA5 && _char.unicode() <= 0x4E00)
	    {
	        return ChineseCharacters;
	    }
	    return UnKonw;
	}
	// 比较先导字符映射表(ps:测试发现先导字符比较不正确,这块需要重写)
	CompareResult comparePlaceholderMap(const QMap<int, int>& placeholderMap1, const QMap<int, int>& placeholderMap2)
	{
	    if (placeholderMap1.isEmpty()&& placeholderMap2.isEmpty())
	    {
	        //全部为空则直接认为相等
	        return Equality;
	    }
		QMap<int, int>::const_iterator index1 = placeholderMap1.begin();
		QMap<int, int>::const_iterator index2 = placeholderMap2.begin();
	    while (1)
	    {
	        //原则1:空小于非空
	        if (index1== placeholderMap1.end()&& index2!= placeholderMap2.end())
	        {
	            return LessThan;
	        }
			if (index1 != placeholderMap1.end() && index2 == placeholderMap2.end())
			{
				return GreaterThan;
			}
	        if (index1 == placeholderMap1.end() && index2 == placeholderMap2.end())
	        {
	            return Equality;
	        }
	  //      //原则2:都非空优先比较数量(在字符串中的位置)
			//if (index1.key() < index2.key())
			//{
			//	return LessThan;
			//}
	  //      if (index1.key() > index2.key())
	  //      {
	  //          return GreaterThan;
	  //      }
	        //原则3:key值相同比较value(按先导字符排序规则)
	        if (index1.value() < index2.value())
	        {
	            return LessThan;
	        }
			if (index1.value() > index2.value())
			{
				return GreaterThan;
			}
	        //原则4:以上规则比较不出大小则去下一个先导字符继续比较
	        index1++;
	        index2++;
	    }
	    //以上规则都比较不出大小则认为相等
	    return Equality;
	}
	
	// 比较字符
	CompareResult compareChar(const QChar& char1, const QChar& char2)
	{
	    //相等直接返回
	    if (char1 == char2)
	    {
	        return Equality;
	    }
	    CharType type1 = getCharType(char1);
	    CharType type2 = getCharType(char2);
	    //原则1:类型不同直接根据类型出结果
	    if (type1 < type2)
	    {
	        return LessThan;
	    }
	    if (type1 > type2)
	    {
	        return GreaterThan;
	    }
	    //原则2:类型相同则根据内容比较大小
	    switch (type1)
	    {
	    case HiChineseStringHelper::Symbol:
		{
	        //符号查表比较大小
	        int value1 = m_symbolTable.value(char1);
	        int value2 = m_symbolTable.value(char2);
			if (value1 < value2)
			{
				return LessThan;
			}
			if (value1 > value2)
			{
				return GreaterThan;
			}
			return Equality;
		}
	    case HiChineseStringHelper::Letter:
	    {
	        //字母
	        //原则1:先统一转成小写尝试比较大小
	        QChar char1Lower = char1.toLower();
	        QChar char2Lower = char2.toLower();
	        if (char1Lower.unicode() < char2Lower.unicode())
	        {
	            return LessThan;
	        }
	        if (char1Lower.unicode() > char2Lower.unicode())
	        {
	            return GreaterThan;
	        }
	        //原则2:相同则比原串unicode,根据ascii大写在前值特点确定大小关系
	        if (char1.unicode() < char2.unicode())
	        {
	            return LessThan;
	        }
			if (char1.unicode() > char2.unicode())
			{
				return GreaterThan;
			}
	        return Equality;
	    }
	    case HiChineseStringHelper::ChineseCharacters:
	    {
	        // 中文字符转拼音比较字符串
	        QString pinyin1 = m_pinyinTable.value(char1);
	        QString pinyin2 = m_pinyinTable.value(char1);
	        return compareQString(pinyin1, pinyin2);
	    } 
		case HiChineseStringHelper::Digit:
	    case HiChineseStringHelper::UnKonw:
	    {
			if (char1.unicode() < char2.unicode())
			{
	            return LessThan;
			}
			if (char1.unicode() > char2.unicode())
			{
				return GreaterThan;
			}
	        return Equality;
	    }
	    default:
	        break;
	    }
	    return Equality;
	}

	//最后:比较两个字符串函数(没判断文件和文件夹类型)
	/*
	参考知乎文档整理排序规则:(优先级递减)
	
	核心规则:
	 1. 文件夹 < 文件:文件夹排在文件之前
	 2. 空字符串 < 非空字符串
	 3. 忽略名称中的前导字符(’ 或 - 或  ——),自左向右依次比较所有字符,字符相同按前导(’ < - < ——)
	
	字符具体排序规则:
	
	 1. 先比较类型:符号 < 数字 < 英文字母 < 汉字
	 2. 类型相同,按下列规则排序:
	- 符号 :(固定顺序)查表
	- 数字:直接比较数值
	- 英文字母:按a-z的顺序排列(先不区分大小写比较,相同则遵循大写在前原则)
	- 汉字:转拼音后按英文字母排序
	*/
	CompareResult compareQString(const QString& name1, const QString& name2)
	{
	    //qDebug() << "name1 : " << name1 << "name2 : " << name2;
	    //步骤1:根据是不是空串比较
	    {
			if (name1.isEmpty() && name2.isEmpty())
			{
				return Equality;
			}
			if (name1.isEmpty() && !name2.isEmpty())
			{
				return LessThan;
			}
			if (!name1.isEmpty() && name2.isEmpty())
			{
				return GreaterThan;
			}
	        //步骤1保证了两个字符串都不为空
	    }
	    // 各自索引
	    int index1 = 0;
	    int index2 = 0;
	    // 字符串长度
	    int lenth1 = name1.length();
	    int lenth2 = name2.length();
	    // 前导符索引映射
	    QMap<int, int> placeholderMap1;
	    QMap<int, int> placeholderMap2;
	    // 获取一个字符
	    QChar char1 = name1.at(index1);
	    QChar char2 = name2.at(index2);
	    //循环步骤2,3,直到得出确定结果后跳出
	    while (1)
	    {
	        // 步骤2:取一个合法的字符(非先导字符),先导字符存表
			{
				// 判断字符是不是前导字符(’ 或 - 或  ——)
	            int ret1 = isPlaceholder(char1);
				for ( ; ret1 != -1; ret1 = isPlaceholder(char1))
				{
					placeholderMap1.insert(index1, ret1);
					if (index1++ == lenth1)
					{
						char1 = QChar();
						break;
					}
					char1 = name1.at(index1);
				}
	            int ret2 = isPlaceholder(char2);
	            for (; ret2 != -1; ret2 = isPlaceholder(char2))
				{
					placeholderMap2.insert(index2, ret2);
					if (index2++ == lenth2)
					{
						char2 = QChar();
						break;
					}
					char2 = name2.at(index2);
				}
			}
			// 步骤3:逐字符比较
			{
				// 根据字符是否为空确定结果
				if (char1.isNull() && !char2.isNull())
				{
					return LessThan;
				}
				if (char2.isNull() && !char1.isNull())
				{
					return GreaterThan;
				}
				if (char2.isNull() && char1.isNull())
				{
	                //根据先导字符确定最终结果
	                return comparePlaceholderMap(placeholderMap1, placeholderMap2);
				}
				//都不为空进行字符比较
	            //不相等->出结果
	            //相等取下一个继续比较
	            CompareResult ret = compareChar(char1, char2);
	            if (ret != Equality)
	            {
	                return ret;
	            }
	            ret = comparePlaceholderMap(placeholderMap1, placeholderMap2);
				if (ret != Equality)
				{
					return ret;
				}
	            placeholderMap1.clear();
	            placeholderMap2.clear();
				char1 = ++index1 == lenth1 ? QChar() : name1.at(index1);
				char2 = ++index2 == lenth2 ? QChar() : name2.at(index2);
			}
	    }
	    return Equality;
	}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值