65.9K
CodeProject 正在变化。 阅读更多。
Home

多遍文件扩展名比较算法

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.75/5 (5投票s)

2007 年 7 月 19 日

LGPL3

5分钟阅读

viewsIcon

37714

downloadIcon

175

当我需要一个文件扩展名比较算法时,就是这样产生的,它应该能为你省去不少思考的时间。

Screenshot - ExtParseAlgorithm.png

引言

这个算法的产生是因为我需要一种快速的方法来查找列表中具有相同扩展名的文件,然后将它们从待比较/解析的扩展名列表中移除,再通过一个for循环系统,最后只返回通过ref(在托管C++中为%)变量返回给程序的(媒体播放器)主处理线所支持的扩展名。

背景

我在本文分享的代码和想法是关于如何快速地完成这种复杂的比较,并且避免使用过多的变量/for循环。它需要速度快,因为它被用在我编写的媒体播放器应用程序的一个包装器中,并且每次打开新文件时都会发生。我的应用程序规范要求它在这个阶段不应该挂起,除非有**极其**充分的理由,而解析文件扩展名在这种情况下并不是应用程序挂起的充分理由。

使用代码

这个算法实际上只有一种方法——一个名为ScanFileExts的函数。

该函数的定义可以在文件“ExtParseAlgorithm.h”(包含在附带的源代码zip文件中)中找到。它看起来像这样:

bool ScanFileExts(array<String^>^ Files, array<String^>^ ComparisonExts,
     array<String^>^% retExts);

它接受两个输入参数:Files - 要解析的文件(带有扩展名),ComparisonExts - 不言而喻,用于解析过程的扩展名。

第三个参数 - retExts - 用于返回支持的**并且**在原始文件列表中的扩展名。

实现的实际定义在下一个文件的顶部,看起来与上面相同,但没有分号,并且有一个注释描述了返回行为。

在另一个名为“ExtParseAlgorithm”的文件中,这是一个C++源文件,其中包含该函数的实际实现。

代码的第一部分(阶段)是:

if (Files->Length == 0) return false;
array<String^>^ ret = gcnew array<String^>(0);
array<String^>^ LoopFileExts = gcnew array<String^>(Files->Length);

// Initial Data Collection
for (int i = 0; i < Files->Length; i++)
{
    String^ Ext = "";
    for (int j = (Files[i]->Length - 1); j > 0; j--)
    {
        if (Files[i][j] == '.')
        {
            Ext = Files[i]->Substring(j);
            j = 0;
        }
    }
    LoopFileExts[i] = Ext;
}
array<String^>^ internFiles = (array<String^>^)LoopFileExts->Clone();

这会测试你提供的文件列表是否为空,如果为空,则返回false表示错误,因为该函数不应该有空参数。然后,它初始化两个内部变量:retLoopFileExts

代码然后从列表中的文件中收集扩展名;它将它们放入另一个变量 - internFiles。有关此代码可能需要解决的行为方面,请参阅“关注点”。

下一个代码阶段是第一次解析/比较,代码如下:

// Checking for sims and diffs (Parse 1)
array< array<bool^>^ >^ Matches = gcnew array< array<bool^>^ >
(Files->Length);
for (int i = 0; i < Files->Length; i++)
{
    // Get matches for each Ext in turn
    array<bool^>^ Matched;
    Matched = gcnew array<bool^>(Files->Length);
    for (int j = 0; j < Files->Length; j++)
    {
        if (j != i)
        {
            if (LoopFileExts[i] == LoopFileExts[j]) Matched[j] = true;
            else Matched[j] = false;
        }
    }
    Matched[i] = false;
    Matches[i] = (array<bool^>^)Matched->Clone();
}

这段代码首先初始化一个布尔数组(声明类型为bool)数组,其大小等于文件数组中文件的数量。下一步是开始遍历这个数组并填充它;这是通过创建一个名为Matched的临时变量来实现的,如果当前文件(由i表示)的扩展名与文件j的扩展名相同,则将每个元素设置为truej是通过运行另一个文件循环创建的变量,然后将j中的数字与i进行比较,如果它们不同,则说明它们是否相同。当前文件(由i表示)在数组中被设置为false;这意味着当文件数组被清理时,如果它是扩展名的第一次出现,则不会被删除。

之后,我们需要缩短文件列表;这通过以下方式实现:

// Shorten the internal file list to just the single extentions
for (int i = 0; ; i++)
{
    array<String^>^ FileList = gcnew array<String^>(0);
    int CurrentFileListLen = internFiles->Length;
    for (int j = 0; j < CurrentFileListLen; j++)
    {
        if (Matches[i][j]->ToString() == bool::FalseString)
        {
            FileList->Resize(FileList, (FileList->Length + 1));
            FileList[(FileList->Length - 1)] = LoopFileExts[j];
        }
    }
    internFiles->Resize(internFiles, 0);
    internFiles->Resize(internFiles, FileList->Length);
    internFiles = (array<String^>^)FileList->Clone();
    break;
}

使用之前创建的数组,它再次通过双重for循环设置进行遍历,移除文件列表中的多余条目。

好了,关于追逐文件列表和清理它就到这里。现在,进行我之前提到的第二次解析,代码如下:

// Do the comparison with the _posible_ extensions (Parse 2)
for (int i = 0; i < internFiles->Length; i++)
{
    for (int j = 0; j < ComparisonExts->Length; j++)
    {
        // Remove excess "\0"'s from the end of the strings.
        if (ComparisonExts[j][(ComparisonExts[j]->Length - 1)] == '\0')
        {
            ComparisonExts[j] = ComparisonExts[j]->Remove((ComparisonExts[j]->Length - 1));
        }
        
        // Perform the comparison
        if (ComparisonExts[j] == internFiles[i])
        {
            ret->Resize(ret, (ret->Length + 1));
            ret[(ret->Length - 1)] = (String^)ComparisonExts[j]->Clone();
        }
    }
}

好了,这是做什么的呢?嗯,首先,使用我一直在使用的双重for循环系统(有些人可能在想这是否是我最喜欢的for循环设置。嗯,不是,如果可能的话,我更喜欢使用单for循环,而且它实际上是我最讨厌的。之所以在这里使用它,是因为它比do-loops和while-loops等其他系统更能胜任手头的任务,哦,还有我最喜欢的for循环设置,单for循环),我们开始比较/解析过程。

这里又创建了一个数组,或者应该说,我们之前创建并初始化为null的返回数组被调整大小,并且新创建的索引被填充了一个扩展名,如果该扩展名与文件列表中的扩展名匹配。

接下来的代码我不想多说了,因为它只会增加应用程序的冗余,而且可以移除而不会破坏算法,但我会说一下它之后的部分,那就是:

// Pass the info back
retExts = ret;

return true; 

这段代码只是将新创建和清理过的文件扩展名列表放入了头文件中提到的唯一返回变量:retExts

这个函数不是void返回类型的原因仅仅是因为我需要一种方法来确定函数是否成功完成了解析。此时,它将返回true表示成功。

当您想调用此算法时,您需要有一个未初始化的变量来接收数据,以及两个类型为array<String^>^的变量,并填入相应的数据;示例包含在源代码中,并在演示版本中编译。源文件是“Main.cpp”,它保持了我为媒体播放器代码设定的风格和规范。

关注点

在初始收集过程中,它会向后计数直到遇到扩展名开头的“.”。如果您想将其用于没有扩展名的文件,它将抛出错误,因为Ext临时变量将不会被填充任何内容;相反,它将是null,当发生克隆时,这会导致.NET抛出null变量异常。(请**不要**这样做,因为它是一个难以发现的错误,如果您像我一样使用它——在一个预编译库(.dll文件)中调用它,而您又无法使用调试版本,因为它根本不起作用。)

历史

  • [2007年7月19日] - 初始文章。
  • [2009年1月23日] - 更新了下载文件,使其可以被普通Windows用户读取。
© . All rights reserved.