Assignment4-Javadoc
1 CsvUtils.java 注释翻译 一个只包含静态方法的类,用于外部排序简化的 CSV 文件
public class CsvUtils
{
private static final Logger logger = Logger.getLogger(CsvUtils.class); //您的“data”目录将位于Eclipse项目目录的顶层,用于此作业 //不要更改名称或将其放在其他位置
private static final Path dataDir = Paths.get(“data”);
/**
* 评分
* @return 你的学生ID */
public static String getStudentID()
{
//将返回值修改为你的学生ID, e.g. // return “1234567”;
return “MY STUDENT ID”;
}
/**
* 评分 *
* @return 你的姓名 */
public static String getStudentName()
{
}
//将返回值修改为你的姓名, e.g. // return “John Smith”; return “MY NAME”;
/**
* 用于返回数据目录路径的访问器方法 *
* @return 数据目录的路径 */
public static Path getDataDir()
{
1
return dataDir;
}
/**
* 一种示例方法,用于显示使用CsvFormatter类读取和写入CSV文件的基本机制。
* 这只是将输入文件复制到输出文件而没有任何更改
* 但是,必须确保使用正确的CSV标头创建输出文件。 *
* @param fromPath
* 要读取的CSV文件的路径 * @param toPath
* 要写入的CSV文件的路径 * @return
* 如果它设法完成而不抛出异常,则为true
* 如果这是一个必须在此任务中实现的空方法
* 则应将返回值保留为false,直到完成它为止
* 以避免对未实现的方法进行不必要的测试 * @throws Exception
* 例外 – 如果出现任何问题,例如 如果你无法打开任何一个文件
* 无法读取fromPath文件
* 无法写入toPath文件
* 如果from文件不符合简化CSV文件格式的要求等 */
public static boolean copyCsv(Path fromPath, Path toPath)
throws Exception
{
// 使用“try-with-resource”模式打开from和to文件
// 这确保了无论返回或异常情况如何
// 两个文件都将自动正确关闭
try (Scanner from = new Scanner(fromPath); PrintWriter to = new
PrintWriter(toPath.toFile()))
{
// 从“from”文件设置CSV格式
CsvFormatter formatter = new CsvFormatter(from);
// 将CSV标题行输出到“to”文件 formatter.writeHeader(to);
// 将每个非标题行从“from”文件复制到“to”文件 String[] row;
while ((row = formatter.readRow(from)) != null)
formatter.writeRow(to, row);
}
return true;
/**
* 将(无序)CSV文件拆分为单独的较小CSV文件(运行
* 其中包含行的排序运行,其中行按columnName参数标识的列的升序排序。 * 这是合并排序的第一个阶段,它生成可以合并在一起的排序运行。
}
2
*
* 这段代码应该适用于真正庞大的文件远远大于我们同时在内存中保存的文件。
* 为了在不使用大文件的情况下模拟这个,我们对numRowLimit参数给出的每次运行的大小施加限制。
* 此外,不应使用内部排序算法:例如 Arrays.sort,Collections.sort,SortedList等。
* 而是必须使用PriorityQueue来生成排序的运行:有一个循环。
* 在循环内部,从输入中读取最多numRowLimit行并将它们插入优先级队列
* 然后按顺序提取它们并将它们写入新的拆分文件。 *
*
* 拆分文件应该是与输入文件相同的文件(即在同一目录中)
* 并且名称为“temp_00000_”,后跟输入文件的名称
* 其中“00000”由序列号替换:“00000“对于第一个拆分文件,”00001“为第二个等 *
* @param fromPath
* 输入文件所在的相对路径
* @param columnName
* 用于排序的列的标题名称
* @param numRowLimit
* 可以写入每个拆分文件的最大值行数(不包括标题行)
* @return 创建分割文件的完整列表的路径的Path []
* @throws Exception
* 如果打开,读取或写入文件出现任何问题,或者输入文件与简化的CSV要求不匹配。 */
public static Path[] splitSortCsv(Path fromPath, String columnName, int numRowLimit)
throws Exception
{
}
Deque
// WRITE YOUR CODE HERE AND REPLACE THE RETURN STATEMENT
//return pathDeque.toArray(new Path[0]);
return null;
/**
* 将两个有序输入CSV文件合并到一个有序输出CSV文件中。
* 两个输入CSV文件必须已经在columnName指定的列上排序
* 并且必须具有相同的CSV格式(相同数量的列,相同顺序的相同标题)
* 输出文件必须具有相同的CSV格式并在同一列上排序。 *
* @param file1Path
* 第一个输入文件的相对路径
* @param file2Path
* 第二个输入文件的相对路径
* @param columnName
* 用于对输出文件进行排序的列,并在其上对两个输入文件进行排序 * @param outputPath
* 输出文件的相对路径
* @return
3
{
} }
Deque
// WRITE YOUR CODE HERE AND REPLACE THE RETURN STATEMENT
return false;
如果已实施此方法,则为true。如果它尚未实现,那么它返回false,这用于导致测试提前失败而不进行不必要的工作 * @throws Exception
*
如果打开,读取或写入文件出现任何问题,或者输入文件与简化的CSV要求不符或具有不同的CSV格式 */
public static boolean mergePairCsv(Path file1Path, Path file2Path, String columnName, Path
outputPath)
{
}
throws Exception
// WRITE YOUR CODE HERE AND REPLACE THE RETURN STATEMENT
return false;
/**
* 将有序输入CSV文件列表合并为单个有序输出CSV文件 *
* 输入CSV文件必须已经在columnName指定的列上排序 *
并且必须具有相同的CSV格式(相同数量的列,相同顺序的相同标题)输出文件必须具有相同的CSV格式并按相同顺序排序。 *
* 此方法应通过在文件对上调用mergePairCsv(…)将所有文件合并在一起
* 从pathList开始,生成越来越大的中间文件
* 直到最后一对成对合并用于生成输出文件。 *
* @param pathList
* 输入文件的相对路径数组
* @param columnName
* 用于对输出文件进行排序的列,并在其上对两个输入文件进行排序 * @param outputPath
* 输出文件的相对路径
* @return
如果已实施此方法,则为true。如果它尚未实现,那么它返回false,这用于导致测试提前失败而不进行不必要的工作 *
* @throws Exception
*
如果打开,读取或写入文件出现任何问题,或者输入文件与简化的CSV要求不符或具有不同的CSV格式 */
public static boolean mergeListCsv(Path[] pathList, String columnName, Path outputPath)
throws Exception
4
2
•
• • • • • •
CsvFormatter.java 注释翻译
一个非常简化的 CSV 格式化程序类。这仅支持非常有限的 CSV 文件格式版本
必须有列标题行和列标题必须是非空字符串,只包含大写和小写字母,数字,下划线,空格和连字
符
字段值中的引号没有特殊含义
行和字段不能分割为多行或包含换行符 非标题行中的字段不能包含逗号字符(’,’)或垂直空格字符(换行符,回车符等) 没有支持的字符转义机制
除逗号(’,’)之外不支持任何字段分隔符
CSV 文件中的所有行必须与标题具有完全相同的字段数(即使某些行为空)
以下是符合以下要求的示例 CSV 文件: ID,Name,Mark,Penalty 1234567,John Smith,87,-3 1234568,Erik Hasselhoff,43,+4 1234569,Xiaowei Zhang,93.5,-2
public class CsvFormatter
{
private static final Logger logger = Logger.getLogger(CsvFormatter.class);
private Map
private String[] headerStrings;
privatestaticPattern headerPat=Pattern.compile(“\\A[-a-zA-Z0-9_]+\\z”); privatestaticPattern valuePat=Pattern.compile(“\\A[^,\\v]*\\z”);
/**
* 基于列标题字符串数组构造CsvFormatter对象 *
* @param headings
* 列标题的String数组
* @throws Exception
* 如果标题包含任何重复项或者任何列标题为null,
* 空或包含除大小写字母,数字,下划线或空格以外的任何字符 */
public CsvFormatter(String[] headings)
throws Exception
{
}
/**
setHeadings(headings);
headerStrings = headings.clone(); //需要克隆它以将类字段与其他类隔离
5
* 从扫描程序读取标题行的构造函数
*
* @param scanner
* 一个打开的Scanner对象,从该对象读取第一行并将其解释为CSV文件的标题行。 * @throws Exception
* 如果扫描仪不是一个打开的扫描仪对象
* 或者从中无法读取其他行,或者它与CSV标题行的要求不匹配。 */
public CsvFormatter(Scanner scanner)
throws Exception
{
}
{
{
if (scanner == null)
throw new Exception(“Error: can’t read from a null scanner!”);
String line;
try
{
line = scanner.nextLine();
}
catch (NoSuchElementException e)
{
throw new Exception(“No further lines in this file”);
catch (IllegalStateException e)
throw new Exception(“Error: scanner is not open!”);
} { }
String[] headings = line.split(“,”, -1); setHeadings(headings);
headerStrings = headings; //不需要克隆它,因为它保证与其他类隔离
/**
* 构造函数的私有辅助函数,用于检查标题字符串是否与所需条件匹配,并相应地设置类字段 *
* @param headings
* 建议的列标题字符串数组
* @throws Exception
* 如果列标题与要求不匹配
*/
private void setHeadings(String[] headings)
throws Exception
for (int i = 0; i < headings.length; i++)
if (headers.containsKey(headings[i]))
throw new Exception("Header row contains duplicate names");
if (headings[i] == null || headings[i].length() == 0)
throw new Exception(String.format("Column header %d is null or empty",
6
} }
{
i));
if (!headerPat.matcher(headings[i]).matches())
throw new Exception(String.format("Column header %d contains invalid
characters: \"%s\"", i, headings[i]));
headers.put(headings[i], i);
/**
* // *返回标题字符串的副本 *
* @return 标题字符串的副本,与内部类字段隔离 */
public String[] getHeaderStrings()
{
return headerStrings.clone();
}
/**
* 获取具有特定标头的列的列号
*
* @param name
* 列标题是必需的
* @return 具有该标头的列的列号,如果该名称不是列标题之一,则为null */
public Integer getColumnNumber(String name)
{
return headers.get(name);
}
/**
* 从打开的扫描仪读取返回CSV行
*
* @param scanner
* 打开扫描仪来读取
* @return 形成行的字段的字符串数组 * @throws Exception
* 如果扫描仪不是一个打开的扫描仪对象
* 或者没有其他行可以读取,或者该行没有与标题相同的字段数 */
public String[] readRow(Scanner scanner)
throws Exception
if (scanner == null)
throw new Exception("Error: can't read from a null scanner!");
try
{
}
if (!scanner.hasNextLine())
return null;
7
catch (IllegalStateException e)
{
throw new Exception("Error: scanner is not open!");
}
String line = scanner.nextLine();
String[] row = line.split(",", -1);
if (row.length != headerStrings.length)
throw new Exception(String.format("Invalid row in CSV file: expected %d fields
but got %d. Line: \"%s\"", headerStrings.length,
row.length, line));
return row;
/**
* 将行写入CSV文件
*
* @param writer
* 打开PrintWriter,通过它来写行
* @param row
* 要写的行
* @throws Exception
* 如果writer或row为null或行与CSV要求不匹配 *
*/
public void writeRow(PrintWriter writer, String[] row)
throws Exception
}
{
if (row == null)
throw new Exception("Attempted to write a null row to CSV file!");
if (row.length != headerStrings.length)
throw new Exception(String.format("Attempted to write invalid row to CSV file:
%d fields required but tried to write %d",
headerStrings.length, row.length));
for (int i = 0; i < row.length; i++)
{
if (!valuePat.matcher(row[i]).matches())
throw new Exception(String.format("Field %d of row contains invalid
characters: \"%s\"", i, row[i]));
}
if (writer == null)
throw new Exception("Attempted to write to a null PrintWriter!");
writer.println(String.join(",", row));
}
/**
* 将标头写入CSV文件 *
8
* @param writer
* 打开PrintWriter,通过它来编写标题 * @throws Exception
* 如果writer是null
*/
public void writeHeader(PrintWriter writer)
throws Exception
{
}
if (writer == null)
throw new Exception("Attempted to write to a null PrintWriter!");
writer.println(String.join(",", headerStrings));
/**
* 此嵌套类是一个比较器类
* 其对象可以根据给定列比较来自相同CSV格式的两个CSV文件行 * (即具有完全相同的列标题和列数)
*/
public class RowComparator implements Comparator
{
/**
* 用于比较的字段的列号 */
private int comparisonField;
/**
* 默认构造函数是私有的,永远不应该使用 * @throws Exception
* 如果它被调用
*/
private RowComparator()
throws Exception
{
}
throw new Exception(“Error: the RowComparator default constructor should never
be called!”);
/**
* 构造一个比较器,可以根据给定列标题指定的列中的字符串顺序比较两行 *
* @param colName
* 用于比较的列的列标题名称
* @throws Exception
如果此CSV文件格式没有作为标题,则给定colName参数 public RowComparator(String colName)
*
*/
{
throws Exception
Integer fieldNum = headers.get(colName);
if (fieldNum == null)
throw new Exception(String.format( 9
} }
}
}
“Error: the comparison column header name \”%s\” does not
match any column header in this CSV file”, colName));
comparisonField = fieldNum;
@Override
public int compare(String[] o1, String[] o2)
{
return o1[comparisonField].compareTo(o2[comparisonField]);
10