从文本文件中读取行,如果值匹配则获取更多行

Read rows from text file, take more lines if value matches

我有一个包含几千行的文本文件。每行是学生和各种测试的测试分数。并非所有学生都有相同数量的测试(即行数)。我需要将文件分解成更小的块,但我不想分解任何一组学生分数。不需要排序,因为主文件已经排序,但我们排序是为了更好的衡量标准。

假设我希望块至少有 5 行,但如果第 6 行与第 5 行是同一个学生,则将第 6 行添加到块中。以此类推,直到学生发生变化。

然后,开始一个新块(使用 headers,但这部分很简单)直到到达原始文件的末尾。

我可以使用 linq 或 FileStream,一旦我有了每个块,我将通过 API.

将它加载到应用程序中

这是主文件的简化示例:

STUDENT_ID    TEST    SCORE
000001          A       10
000001          B       10
000001          C       10
000001          D       10
000002          A       10
000002          B       10
000002          C       10
000002          D       10
000003          A       10
000003          B       10
000004          C       10
000004          D       10
000004          E       10
000004          F       10

所以,第一个块看起来像:

STUDENT_ID    TEST    SCORE
000001          A       10
000001          B       10
000001          C       10
000001          D       10
000002          A       10
000002          B       10
000002          C       10
000002          D       10

到目前为止,我已经完成了一个 While 循环,它使用常量 "rowsToTake" = 5,一个比较第 5 行的 STUDENT_ID 的子字符串 (0, 6),以及一个 "currentPosition" 每次拍摄都会增加。我在获取后续块的外循环上失去了动力。到目前为止,我选择不 post 我的代码,因为我认为它不好,而且我不希望任何人觉得他们应该以此为基础。

我认为 LINQ 解决方案不适合您的情况。我更喜欢使用 for 循环,相应地比较文本文件中每一行的内容。


伪代码:

string previousStudentID = null;
List<...> chunk = new List<...>();
foreach (string line in file)
{
    string studentID = // parse studentID from line

    if (studentID != previousStudentID && chunk.Count > 5)
    {
        // add header to beginning of chunk
        // load chunk to API

        chunk.Clear(); // clear/create a new chunk
    }

    // add line to chunk

    previousStudentID = studentID;
}

// load remaining header/chunk to API, if necessary

使用 LINQ 按学生 ID 分组效率不高,所以在排队时处理似乎更好。

public class TestRecord {
    public string line;
    public string StudentID;

    public TestRecord(string _line) {
        line = _line;
        StudentID = Regex.Split(line, @"\s+")[0];
    }
}

public IEnumerable<TestRecord> ReadRecords(string filename) {
    var fileInput = File.ReadLines(filename);

    foreach (var line in fileInput)
        yield return new TestRecord(line);
}

void Main() {
    var filePath = @"file folder\";
    var recordInput = ReadRecords($"{filePath}students.txt");
    var inputEnumerator = recordInput.GetEnumerator();

    // get header line
    inputEnumerator.MoveNext();
    var headerLine = inputEnumerator.Current.line;
    inputEnumerator.MoveNext();

    var chunkSize = 5;

    var outFileCount = 0;
    var chunkSoFar = 0;
    StreamWriter outFile = null;
    bool moreInput;
    do {
        if (chunkSoFar >= chunkSize || chunkSoFar == 0) { // start new chunk
            outFile?.Close();
            outFile = new StreamWriter($"{filePath}chunk{++outFileCount:D4}.txt".Dump());
            outFile.WriteLine(headerLine.Dump());
            chunkSoFar = 0;
        }
        string curStudentID = inputEnumerator.Current.StudentID;
        do {
            outFile.WriteLine(inputEnumerator.Current.line.Dump());
            ++chunkSoFar;
        } while ((moreInput = inputEnumerator.MoveNext()) && inputEnumerator.Current.StudentID == curStudentID);
    } while (moreInput);
    outFile.Close();
}