从文本文件中读取行,如果值匹配则获取更多行
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();
}
我有一个包含几千行的文本文件。每行是学生和各种测试的测试分数。并非所有学生都有相同数量的测试(即行数)。我需要将文件分解成更小的块,但我不想分解任何一组学生分数。不需要排序,因为主文件已经排序,但我们排序是为了更好的衡量标准。
假设我希望块至少有 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();
}