我的朋友给我发了这个,我找不到办法去做。
的数据
我用人工智能做过无数次尝试,但都没有希望。如果有人能帮助我,我将非常感激。它必须是C++
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <unordered_map>
#include <regex>
// Function to split a string based on a delimiter
std::vector<std::string> splitString(const std::string& s, char delimiter) {
std::vector<std::string> tokens;
std::stringstream ss(s);
std::string token;
while (std::getline(ss, token, delimiter)) {
tokens.push_back(token);
}
return tokens;
}
int main() {
// Input and output file paths
const std::string inputFile = "file_list.csv";
const std::string outputFile = "output.csv";
// Open the input file
std::ifstream inFile(inputFile);
if (!inFile.is_open()) {
std::cerr << "Error opening file: " << inputFile << std::endl;
return 1;
}
// Create a map to store information about each student's group and questions answered
std::unordered_map<std::string, std::pair<std::string, std::vector<int>>> studentInfo;
// Regular expression pattern for extracting information
std::regex pattern(R"(([a-zA-Z]+)_?(\d+)_?(\d+)?.*)");
// Read the input file line by line
std::string line;
while (std::getline(inFile, line)) {
// Use regex to match the pattern
std::smatch match;
if (std::regex_match(line, match, pattern)) {
// Extract matched groups
std::string groupName = match[1];
std::string studentID = match[2];
std::string questionNumberStr = match[3];
// Convert question number to integer
int questionNumber = (questionNumberStr.empty()) ? -1 : std::stoi(questionNumberStr);
// Update the map with the student's information
if (!studentID.empty()) {
if (studentInfo.find(studentID) == studentInfo.end()) {
// If the student is not in the map, add a new entry
studentInfo[studentID] = std::make_pair(groupName, std::vector<int>{questionNumber});
} else {
// If the student is already in the map, update the existing entry
if (questionNumber != -1) {
studentInfo[studentID].second.push_back(questionNumber);
}
}
}
}
}
// Close the input file
inFile.close();
// Open the output file for writing
std::ofstream outFile(outputFile);
if (!outFile.is_open()) {
std::cerr << "Error opening file: " << outputFile << std::endl;
return 1;
}
// Write the header to the output file
outFile << "StudentList,GroupName,QuestionsAnswered" << std::endl;
// Write the student information to the output file
for (const auto& entry : studentInfo) {
// Sort the list of questions answered by each student
std::vector<int> questions = entry.second.second;
std::sort(questions.begin(), questions.end());
// Write the student information to the output file
outFile << entry.first << "," << entry.second.first << ",";
for (size_t i = 0; i < questions.size(); ++i) {
outFile << questions[i];
if (i < questions.size() - 1) {
outFile << ",";
}
}
outFile << std::endl;
}
// Close the output file
outFile.close();
std::cout << "Output file created successfully: " << outputFile << std::endl;
return 0;
}
字符串
我找不到一种方法将qrub、grup或group words从group列的元素中分离出来。它必须是一个像示例一样的csv文件。
file_list.csv的一部分
d_3_2211011228.cpp
d_3_2211012211.cpp.txt
D_3_2211011054.cpp.txt
d_3_2211011096 .txt
d_question1_2111011034.txt
d_question2_2111011034.txt
d_question3_2111011034.txt
Group a_1_2211011032.cpp
Group a_2_2211011032.cpp
Group a_3_2211011032.cpp
group c_QUESTION 1_2211011024.txt
型
4条答案
按热度按时间qxgroojn1#
我无法找到一种方法将qrub、grup或group words从group列的元素中分离出来。
一种方法是将每一行分成三部分,使用下划线(
_
)作为下划线。在这里的演示中,我使用了结构
Fields
,其中所有的成员都是字符串,这些是从一行输入中解析出来的“原始”字段。为了方便起见,我使用 hidden friends 习惯用法为struct
Fields
提供了一个流插入操作符。字符串
给定输入文件中的一行,函数
parse_fields
提取这三个字段(作为字符串),并将它们放入Fields
结构中。该结构作为引用参数传递。如果解析成功,函数
parse_fields
返回true
。如果解析失败,则向std::cerr
输出错误消息,parse_fields
返回false
。型
解析完字段后,进一步的处理可以将每个字段转换为最终形式。
group
可能是最简单的。只需将其最后一个字符转换为小写,并丢弃所有其他字符。其他字段需要更多的工作。对于
question
number,我可能会使用std::string
中的几个成员函数。使用"0123456789"
作为search-for参数调用这些函数:find_last_of
find_last_not_of
的明确了
question
的位置后,调用成员函数substr
将为您提取它。使用
find_first_of
和find_first_not_of
的类似技术将得到student_id
。I can't take all the fun
当然,这仍然有很多工作要做,你必须在Map中找到
student_id
和group
,然后将question
推到问题向量的后面,等等。但我得给你留点乐子!
演示程序
下面是一个简短的演示程序。
我运行了一个截短版本的数据文件从谷歌组:
型
该程序包括我工具箱中的几个函数:
to_lower_in_place
和trim_whitespace
它们相对简单,正如你在源代码中看到的那样。除此之外,我添加了函数parse_header
以跳过数据文件的第一行。函数
main
打开文件,并在循环中读取记录,当文件结束时停止。型
下面是输出:
型
g0czyy6m2#
最主要的问题是正则表达式不正确。下面是我修改的程序。
字符串
wvt8vs2t3#
C++解析文本并写入CSV文件
这看起来更像是一种谜题,而不是学生的任务。无论如何,我觉得这很奇怪。我将在这里展示一种使收支平衡的方法,并在
C++
中留下一个示例和输出。我的阅读绝不是最终的结论,因为描述似乎不够正式,不足以得出一个结论(或者说,我可以看到足够正式的结论)。输入输出文件
看起来我们有一个输入
csv
文件,每个答案有一行,3个字段和一个'_'
作为输入。预期的输出是一个合成csv
文件,每个学生显示一行,对答案进行分组。输出的排序标准是 * stable *,这意味着学生按照他们在输入中出现的顺序列出。在原始的完整输入
csv
中,作为Google Drive中的链接提供,甚至还有一个标题 *File Name
*,但我认为这只是为了分散读者的注意力。没有文件,没有内容,文本就是这样:第三个字段以基本的学号开始,如果我们将文件视为使用下划线作为下划线的3字段csv
,但它伪装成一个1行有趣的csv
,带有 * 文件名 *,用于没有意义的文件。至于
csv
文件是什么,RFC4180 Common Format and MIME Type for Comma-Separated Values (CSV) Files是参考按照提供的方式输入
csv
文件字符串
并且该预期输出如图所示
[![这显示了提取数据的方法][1]][1]
这样做似乎只是为了迷惑学生,并测试他/她解析文本的能力,无论如何。它不是一个1字段
csv
文件中的文件列表。它只是一个简单的3字段csv
和一个_
文件。它列出了3个学生的4个答案。
2
的组a
中,而问题3
的组A
中任务描述输出
csv
根据这一描述(以及上述注解):
型
输出
csv
也是一个3字段文件。所以在这个例子中,我将
_
作为对于
csv
没有任何说明,但很明显,需要一个标题行,因为我们在来自问题提供的代码
提供的代码更多的是在
C
方面,而不是在C++
方面。因此,作为一个csv
解析器,奇怪的是没有看到scanf
的使用,而是看到regex
包含在内。正则表达式是一个很棒的资源,但这里的表达式是固定的,简单的,所以我认为在这种情况下,regex
更多的是一个问题而不是一个解决方案。当然,对一个同样匹配正则表达式的向量进行排序也是算法的一种情况。
for_each
将是一个实际上只有一行的解析器的基本选择。但是,看看任务描述,很明显输出不能排序。学生的顺序在输出中保持:只是每个学生的额外答案被分组在最后一个字段中。请参见示例中的student2111011011
的情况。注意,顺序是保留的。代码似乎比需要的要复杂一些,但它似乎几乎是一个解决方案。
使用Google表格测试原始输入数据
这是原始问题的数据:
型
在Sheets中使用
File | Import
--当然使用-
作为模板--我们得到型
因此,看来解释是正确的:3个字段,都可以。我们只需要插入字段名称并按学生分组答案。
C++
和问题的抽象因为顺序是要保持的,所以在提供的代码中,
unordered_map
似乎是一个自然的选择,作为一个 * 容器 *。每个学生的答案也可以存储在一个向量中,但是,因为我们不需要测试输入中的唯一值,我们可以只使用一个字符串并附加每个学生的答案。第一个原型
下面的程序获取一个名为argumen的文件并解析它,在屏幕上显示结果
型
为此文件
它显示
型
创建
csv
文件既然它看起来没问题,我们可以使用一个简单的
struct
来保存Map,并使用一个函数来编写csv
文件。Answers.h
中的简单结构Answers.cpp
中的实现这是一个简单的问题:
Answers()
获取文件名并将数据加载到Map中show()
在屏幕上显示数据create_csv()
获取一个文件名,并将Map作为csv
文件卸载到磁盘。insert_if_ok()
解析数据并上传。main
进行测试简单测试的输出
使用驱动器中的原始
csv
运行原始文件
Tks @tbxfreeware为张贴链接到这个文件.这是一个极简
csv
与1字段和174记录.它似乎与文件名,甚至有一个标题 “文件名” 但我相信这只是一个分心的学生.没有文件,没有内容,只是一个文本,在每行的第二条下划线之后是10位数的学生编号。有些行有额外的单词和/或额外的空格,没有正式的描述应该有什么,也没有描述在感知到 * 错误 * 的情况下该怎么做。无论如何,这是文件(在写这篇文章的时候):
此文件的示例代码输出
一些行被删除,因为完整的输出和
csv
只是一个运行程序的问题.和产生的csv
可以导入到任何程序.它只是在这里导入一次到Microsoft Excel.注意上面的代码:
tp5buhyn4#
我找不到一种方法将qrub、grup或group单词与group列的元素分离。
一种方法是使用下划线(
_
)作为分隔符,将每行分为三个部分。这个答案描述了我编写的程序,它使用两个主要类
StudentAnswers
和FileList
从OP中读取CSV文件。StudentAnswers
-管理包含输入文件中数据的std::map
。它还包含一个将Map写入输出文件的函数。FileList
-包含读取输入文件的解析函数。这个答案很长。我建议你先阅读一点,然后再决定你的兴趣是否被激发了。否则,你可以随时退出!
完整程序的源代码从第StudentAnswers节开始。
FileList部分介绍了程序核心的解析算法。
输入文件的布局
问题规范中留下了输入文件布局的许多细节。我已经通过基于发布到Google Drive的sample data file中发现的数据进行假设来填充它们。
1.输入文件为CSV,其中没有引用字段。
1.每条记录都是一行,其中包含一个字段。
1.第一行是信头,包含字段名称,即
File Name
。1.后续的行包含数据。每行都有一个文件名。
文件名可以分为三个部分,但都不包含下划线字符(
'_'
)。下划线字符用作分隔符,出现在第一部分和第二部分之间,也出现在第二部分和第三部分之间。group_name
-此部分可以包含任意数量的字符。最后一个字母字符在转换为小写char
时为group_name
。所有其他字符都将被丢弃。question_number
-此部分可以包含任意数量的字符。当转换为int
类型时,最后一系列数字字符为question_number
。因此,它是最接近此部分结尾的数字。所有其他字符都将被丢弃。student_id
-此部分可以包含任意数量的字符。最接近开头的一系列数字(作为字符串)是student_id
。所有其他字符都将被丢弃。应用程序使用的Map
示例数据文件中有三个学生属于两个组。学生
2211011080
是b
和c
的成员。同样,2211011048
属于c
和d
,而2111011094
属于a
和c
。基于此,我假设一个学生可以属于多个组。因此,下面定义的Map将使用同时包含
group_name
和student_id
的级联键。这些平凡的型别别名是设计用来改善下列定义的可读性。
字符串
结构
GroupStudentKey
定义将在Map中使用的关键字。它将group_name
放在student_id
之前,以便Map首先按组排序,然后在每个组内按学生排序。“spirpace”运算符(<=>
)使编译器创建强制执行此排序的默认比较运算符。型
类
QuestionsAnswered
管理一个QuestionNumber
对象的向量,其中每个QuestionNumber
对象都是一个int
。它是Map所使用的元素类型。QuestionsAnswered
类有六个成员函数:sort
-按升序对问题编号的向量进行排序。operator()
-返回对向量的引用。operator() const
-返回对向量的常量引用。operator== const
-默认相等运算符operator<<
-将问题编号输出到流。operator>>
-输入流中的问题编号。现在我们有了Map所使用的类型,它的键为
GroupStudentKey
,并且包含QuestionsAnswered
对象的元素。型
学生回答
类
StudentAnswers
封装了一个map对象,并提供了用于管理它的最小工具集。文件
StudentAnswers.h
包含它的声明,沿着上面给出的Map定义。型
类
StudentAnswers
的成员函数大多数是自扩展的。一个重要的组处理文件I/O。read_csv
-读取由成员函数write_csv
写入的CSV文件。清除Map,然后从文件中加载数据。此函数包括许多检查,当输入文件中的某个记录无法解析时,它可以给予详细的错误消息。write_csv
-写入一个CSV文件,其中包含Map中的字段student_id
、group_name
和questions_answered
。operator()
的两个版本(一个是常量,另一个是非常量)返回对Map的引用。operator()
-传回对映的指涉。operator() const
-返回对Map的常量引用。唯一的其他成员函式如下:
sort
-在Map上进行一次遍历,并为每个元素调用questions_answered.sort()
。exit_msg
-传回描述做为参数提供之exit_code
的std::string
。这些是由成员函数read_csv
和write_csv
传回的结束码。parse_header
-读取CSV文件的第一行。parse_field_name
-parse_header
的辅助函数型
文件列表
FileList
类是一个文件读取器。read_csv
接受一个StudentAnswers
对象作为参数,并通过解析输入文件file_list.csv
来填充它。file_list.csv
中的记录具有本答案开头所述的布局。语法分析并不难,但这是一项非常精细的工作,如果你不注意的话,很容易犯一个接一个的错误。
成员函数
read_csv
运行一个循环,每次迭代从输入文件中读取一行,并将其存储在字符串变量record
中。解析例程对变量
record
进行一次传递,使用变量pos
来跟踪当前位置。pos
是record
中下一个未解析字符的下标。pos
作为引用参数依次传递给每个解析例程。当其中一个例程更新它时,其他例程将看到变化。解析例程大量使用
std::string
类中的find
函数。当搜索失败时,它们都返回sentinelstd::string::npos
。第一对用于查找下划线。
record.find('_')
-返回record
中第一个下划线的下标。record.find('_', pos)
-返回record
中“next”下划线的下标。开始搜索位置pos
。下一组用于查找 * 数字串 *。前两组从位置
pos
向前搜索,用于解析student_id
。record.find_first_of("0123456789", pos)
-从位置pos
开始向前搜索,并返回遇到的第一个数字的下标。record.find_first_not_of("0123456789", pos)
-从位置pos
开始向前搜索,并返回遇到的第一个非数字的下标。接下来的两个从位置
pos
向后搜索,并用于解析question_number
。record.find_last_of("0123456789", pos)
-从位置pos
开始向后搜索,并返回遇到的第一个数字的下标。record.find_last_not_of("0123456789", pos)
-从位置pos
开始向后搜索,并返回遇到的第一个非数字的下标。在函数
parse_group_id
中,策略是找到第一个下划线,然后从那里向后搜索,寻找一个字母字符。找到的第一个字符被转换为小写,并存储为group_id
。函数
parse_question_number
首先查找下一个下划线,然后从那里向后搜索两次。第一次向后搜索查找question_number
中的最后一个数字。第二次向后搜索查找question_number
中第一个数字之前的非数字字符。在
question_number
中的开始和结束数字的位置被确定之后,对std::from_chars
的调用将它们转换为int
。解析
student_id
与解析question_number
类似,不同之处在于您从第二个下划线向前搜索,而不是向后搜索。不需要转换为int
。调用substr
将student_id
提取为字符串。型
函数
main
给定类
StudentAnswers
和FileList
,编写函数main
是一件小事。它调用这些类的读和写函数,并报告exit_code
。工具箱中的一些函数
输入
我运行了两次程序,一次是针对完整的输入文件,一次是针对截断的文件。
完整的文件
file_list.csv
(来自Google Drive)包含174条记录。被截断的文件只有十几条记录。被截断的文件中的几个学生属于多个组。几个学生回答了多个问题。
输出
此程序的输出被发送到两个文件:
output.csv
-Map的内容。这是成功解析的数据。此文件包含三个字段:StudentID
、GroupName
和QuestionsAnswered
。skipped.csv
-任何无法解析的记录都将写入此文件。在程序的两次运行中,没有发生解析错误。文件
skipped.csv
在两次运行后都是空的。下面是从截断的输入文件生成的输出: