C#& LINQ;将文件分组为5MB组

时间:2010-12-30 17:03:01

标签: c# linq

问候,我正在尝试编写Linq查询以在文件名列表上运行,该文件名返回分组为5MB块的文件列表。因此,每个组都将包含一个文件名列表,其总和/总计MB最大为5MB。

我对Linq没事,但是这个我不知道从哪里开始。帮助

DirectoryInfo di = new DirectoryInfo (@"x:\logs");
List<string> FileList = di.GetFiles ("*.xml")
var Grouped = FileList =>

4 个答案:

答案 0 :(得分:1)

是的,你可以用LINQ做到这一点。

var groupedFiles = files.Aggregate(
    new List<List<FileInfo>>(),
    (groups, file) => {
        List<FileInfo> group = groups.FirstOrDefault(
           g => g.Sum(f => f.Length) + file.Length <= 1024 * 1024 * 5
        );
        if (group == null) {
            group = new List<FileInfo>();
            groups.Add(group);
        }
        group.Add(file);
        return groups;
    }
);

这个算法很贪心。它只是找到了第一个列表,它可以将FileInfo推进而不会超过5MB的上限。就最小化群组数而言,它并不是最优的,但您没有将其作为约束。我认为在致电OrderBy(f => f.Length)之前Aggregate会有所帮助,但我现在没有时间深入思考这个问题。

答案 1 :(得分:0)

首先看this StackOverflow question。它将分组分配到子列表中。然后诀窍是检测group by子句中文件的大小。这可能是一个答案,使用LINQ可能比使用它更清晰。

部分问题是您有一个文件名称列表。您需要一个File对象列表,以便通过LINQ查询文件的大小。在Linq 4.0中,你有一个group-by-into构造,应该是你想要的。

答案 2 :(得分:0)

这是一种方式:

  1. 定义一个将文件大小作为输入的类型,并返回一个值,该值在达到指定的最大值时递增并重置。 (此类型负责维护自己的状态。)
  2. 按此类型返回的值进行分组。
  3. 代码示例:

    // No idea what a better name for this would be...
    class MaxAmountGrouper
    {
        readonly int _max;
    
        int _id;
        int _current;
    
        public MaxAmountGrouper(int max)
        {
            _max = max;
        }
    
        public int GetGroupId(int amount)
        {
            _current += amount;
            if (_current >= _max)
            {
                _current = 0;
                return _id++;
            }
    
            return _id;
        }
    }
    

    用法:

    const int BytesPerMb = 1024 * 1024;
    
    DirectoryInfo directory = new DirectoryInfo(@"x:\logs");
    FileInfo[] files = directory.GetFiles("*.xml");
    
    var grouper = new MaxAmountGrouper(5 * BytesPerMb);
    var groups = files.GroupBy(f => grouper.GetGroupId((int)f.Length));
    
    foreach (var g in groups)
    {
        long totalSize = g.Sum(f => f.Length);
        Console.WriteLine("Group {0}: {1} MB", g.Key, totalSize / BytesPerMb);
        foreach (FileInfo f in g)
        {
            Console.WriteLine("File: {0} ({1} MB)", f.Name, f.Length / BytesPerMb);
        }
        Console.WriteLine();
    }
    

答案 3 :(得分:-1)

我首先将文件列表抛出到SQL表中。这样的东西,但包括大小列:

CREATE TABLE #DIR (fileName varchar(100))

INSERT INTO #DIR
EXEC master..xp_CmdShell 'DIR C:\RTHourly\*.xml /B'

然后它将是一个选择语句,如:

SELECT *,
CASE WHEN SIZE < 5 THEN 1
WHEN SIZE < 10 THEN 2
...
END AS Grouping
FROM #DIR
ORDER BY Grouping, FileName, Size

有一个安全设置,您必须在SQL Server上快速更改才能执行此操作。请参阅博文HERE.