404 Not Found

404 Not Found


nginx

Practice: LINQ and Files

Student Grade Analyzer

Serialize student data to a JSON file, then deserialize it and use LINQ for multi-dimensional statistical analysis: average score, top three, pass count, and grouping by grade level.

Requirements

Example

CSHARP
using System;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Collections.Generic;

public class Student
{
    public string Name { get; set; }
    public int Score { get; set; }
}

class Program
{
    static void Main()
    {
        var students = new List<Student>
        {
            new Student { Name = "Zhang San", Score = 92 },
            new Student { Name = "Li Si", Score = 45 },
            new Student { Name = "Wang Wu", Score = 78 },
            new Student { Name = "Zhao Liu", Score = 88 },
            new Student { Name = "Qian Qi", Score = 55 },
            new Student { Name = "Sun Ba", Score = 95 },
            new Student { Name = "Zhou Jiu", Score = 33 },
            new Student { Name = "Wu Shi", Score = 71 }
        };

        string tempDir = Path.GetTempPath();
        string filePath = Path.Combine(tempDir, "students.json");

        var options = new JsonSerializerOptions { WriteIndented = true };
        string json = JsonSerializer.Serialize(students, options);
        File.WriteAllText(filePath, json);
        Console.WriteLine("Written to: " + filePath);

        string readJson = File.ReadAllText(filePath);
        var loaded = JsonSerializer.Deserialize<List<Student>>(readJson);

        double average = loaded.Average(s => s.Score);
        Console.WriteLine($"Average score: {average:F1}");

        var top3 = loaded.OrderByDescending(s => s.Score).Take(3);
        Console.WriteLine("Top three:");
        foreach (var s in top3)
        {
            Console.WriteLine($"  {s.Name} - {s.Score}");
        }

        int passCount = loaded.Count(s => s.Score >= 60);
        Console.WriteLine($"Pass count: {passCount}");

        var grouped = loaded.GroupBy(s => s.Score >= 90 ? "Excellent"
            : s.Score >= 60 ? "Pass" : "Fail");
        Console.WriteLine("Grade groups:");
        foreach (var group in grouped)
        {
            Console.WriteLine($"  {group.Key}: {string.Join(", ", group.Select(s => s.Name))}");
        }

        File.Delete(filePath);
    }
}
▶ Try it Yourself
TEXT
Written to: /tmp/students.json
Average score: 69.6
Top three:
  Sun Ba - 95
  Zhang San - 92
  Zhao Liu - 88
Pass count: 5
Grade groups:
  Excellent: Zhang San, Sun Ba
  Fail: Li Si, Qian Qi, Zhou Jiu
  Pass: Wang Wu, Zhao Liu, Wu Shi

Simple Log Analyzer

Read a text log file, parse each line, and use LINQ to count errors/warnings, find the most frequent error, and filter by date range.

Requirements

Example

CSHARP
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        string tempDir = Path.GetTempPath();
        string logPath = Path.Combine(tempDir, "app.log");

        var lines = new List<string>
        {
            "[2025-01-10] [INFO] System started",
            "[2025-01-10] [WARN] Memory usage 80%",
            "[2025-01-11] [ERROR] Database connection failed",
            "[2025-01-11] [ERROR] Disk full",
            "[2025-01-12] [INFO] User login",
            "[2025-01-12] [ERROR] Database connection failed",
            "[2025-01-13] [WARN] CPU usage 90%",
            "[2025-01-13] [ERROR] Network timeout",
            "[2025-01-14] [INFO] Scheduled task completed",
            "[2025-01-14] [ERROR] Database connection failed"
        };
        File.WriteAllLines(logPath, lines);

        var logEntries = File.ReadAllLines(logPath)
            .Select(line =>
            {
                var parts = line.Split(']');
                return new
                {
                    Date = parts[0].TrimStart('[').Trim(),
                    Level = parts[1].TrimStart('[').Trim(),
                    Message = parts[2].Trim()
                };
            })
            .ToList();

        var levelCounts = logEntries
            .GroupBy(e => e.Level)
            .Select(g => new { Level = g.Key, Count = g.Count() })
            .OrderByDescending(x => x.Count);
        Console.WriteLine("Level counts:");
        foreach (var item in levelCounts)
        {
            Console.WriteLine($"  {item.Level}: {item.Count}");
        }

        var topError = logEntries
            .Where(e => e.Level == "ERROR")
            .GroupBy(e => e.Message)
            .OrderByDescending(g => g.Count())
            .First();
        Console.WriteLine($"Most frequent error: {topError.Key} ({topError.Count()} times)");

        var filtered = logEntries
            .Where(e => string.Compare(e.Date, "2025-01-11") >= 0
                     && string.Compare(e.Date, "2025-01-13") <= 0)
            .ToList();
        Console.WriteLine("Logs from Jan 11-13:");
        foreach (var entry in filtered)
        {
            Console.WriteLine($"  [{entry.Date}] [{entry.Level}] {entry.Message}");
        }

        File.Delete(logPath);
    }
}
▶ Try it Yourself
TEXT
Level counts:
  ERROR: 5
  INFO: 3
  WARN: 2
Most frequent error: Database connection failed (3 times)
Logs from Jan 11-13:
  [2025-01-11] [ERROR] Database connection failed
  [2025-01-11] [ERROR] Disk full
  [2025-01-12] [INFO] User login
  [2025-01-12] [ERROR] Database connection failed
  [2025-01-13] [WARN] CPU usage 90%
  [2025-01-13] [ERROR] Network timeout

Batch File Rename Tool

List files in a directory, filter by extension using LINQ, then batch-add a prefix or suffix.

Requirements

Example

CSHARP
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        string workDir = Path.Combine(Path.GetTempPath(), "rename_demo");
        Directory.CreateDirectory(workDir);

        var demoFiles = new[] { "photo1.jpg", "photo2.jpg", "doc.txt", "notes.txt", "data.csv" };
        foreach (var f in demoFiles)
        {
            File.WriteAllText(Path.Combine(workDir, f), "demo");
        }
        Console.WriteLine("Original files:");
        foreach (var f in Directory.GetFiles(workDir))
        {
            Console.WriteLine("  " + Path.GetFileName(f));
        }

        string targetExt = ".txt";
        string prefix = "backup_";

        var filesToRename = Directory.GetFiles(workDir)
            .Where(f => Path.GetExtension(f).Equals(targetExt, StringComparison.OrdinalIgnoreCase))
            .ToList();

        Console.WriteLine($"\nFiltering extension {targetExt}:");
        foreach (var f in filesToRename)
        {
            string dir = Path.GetDirectoryName(f);
            string nameNoExt = Path.GetFileNameWithoutExtension(f);
            string newName = prefix + nameNoExt + targetExt;
            string newPath = Path.Combine(dir, newName);
            Console.WriteLine($"  {Path.GetFileName(f)} -> {newName}");
            File.Move(f, newPath);
        }

        Console.WriteLine("\nAfter renaming:");
        foreach (var f in Directory.GetFiles(workDir))
        {
            Console.WriteLine("  " + Path.GetFileName(f));
        }

        Directory.Delete(workDir, true);
    }
}
▶ Try it Yourself
TEXT
Original files:
  photo1.jpg
  photo2.jpg
  doc.txt
  notes.txt
  data.csv

Filtering extension .txt:
  doc.txt -> backup_doc.txt
  notes.txt -> backup_notes.txt

After renaming:
  photo1.jpg
  photo2.jpg
  backup_doc.txt
  backup_notes.txt
  data.csv

❓ FAQ

Q What if property names don't match during JSON deserialization?
A Use the [JsonPropertyName] attribute to map names, or customize PropertyNamingPolicy in JsonSerializerOptions.
Q Is the order of GroupBy results guaranteed?
A No, GroupBy does not guarantee group order. Use OrderBy to sort before iterating.
Q What happens if the target file already exists when using File.Move to rename?
A It throws an IOException. You should check whether the target path exists first, or use File.Replace.
Q Is ReadAllLines appropriate for reading large log files?
A No, ReadAllLines loads all content into memory at once. For large files, use File.ReadLines for lazy, line-by-line reading.

📖 Summary

📝 Exercises

  1. Extend the student grade analyzer to count students by score range (0-59/60-79/80-89/90-100) and output a bar chart (using * characters)
  2. Add a feature to the log analyzer: count ERROR entries per day and output the date range with the most consecutive ERROR occurrences
  3. Refactor the file rename tool to accept command-line arguments for directory path, extension filter, and prefix, and add a --dry-run mode that only shows a preview without executing
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏