Java 根据列日期对 csv 文件进行排序
Java sort a csv file based on column date
需要根据日期列对 csv 文件进行排序。这是 masterRecords 数组列表的样子
GBEP-1-2-4,FRAG,PMTypeEthernet,NEND,TDTN,15-MIN,Dec 15 2014 - 07:15:00 AM MYT,+0,COMPL
GBEP-1-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15-MIN,Dec 15 2014 - 07:00:00 AM MYT,+0,COMPL
GBEP-2-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15-MIN,Dec 15 2014 - 07:30:00 AM MYT,+0,COMPL
我需要根据日期 07:15:00、07:30:00 等进行排序。我创建了一个代码来进行排序:
// Date is fixed on per 15min interval
ArrayList<String> sortDate = new ArrayList<String>();
sortDate.add(":00:");
sortDate.add(":15:");
sortDate.add(":30:");
sortDate.add(":45:");
BufferedWriter bw = new BufferedWriter(new FileWriter(tempPath + filename));
for (int k = 0; k < sortDate.size(); k++) {
String date = sortDate.get(k);
for (int j = 0; j < masterRecords.size(); j++) {
String[] splitLine = masterRecords.get(j).split(",", -1);
if (splitLine[10].contains(date)) {
bw.write(masterRecords.get(j) + System.getProperty("line.separator").replaceAll(String.valueOf((char) 0x0D), ""));
masterRecords.remove(j);
}
}
}
bw.close();
从上面可以看出,它将遍历第一个数组 (sortDate) 并在第二个数组(即 masterRecord)上再次循环并将其写入新文件。它似乎在整理新文件时工作,但我注意到我的 masterRecord 有 10000 条记录,但在创建新文件后记录缩小到 5000,我假设它是如何从主列表中删除记录的。有人知道为什么吗?
在循环中删除项目是不安全的。
您必须在迭代器上迭代数组,例如:
List<String> names = ....
Iterator<String> i = names.iterator();
while (i.hasNext()) {
String s = i.next(); // must be called before you can call i.remove()
// Do something
i.remove();
}
文档说:
The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
by Lautaro Cozzani 正确。
现在来点完全不同的东西
为了好玩,这是一种完全不同的方法。
我使用了两个库:
- 阿帕奇 Commons CSV
- Joda-Time
Apache Commons CSV
Commons CSV 库处理各种 CSV 风格的解析。它可以 return 文件中行的列表,每一行由它们的 CSVRecord
对象表示。您可以向该对象询问第一个字段、第二个字段等。
乔达时间
Joda-Time 负责解析日期时间字符串。
避免使用 3 个字母的时区代码
注意:Joda-Time 拒绝尝试解析三字母时区代码 MYT
。有充分的理由:那些 3 或 4 个字母的代码只是约定俗成,既不标准化也不唯一。我下面的示例代码假设您的所有数据都使用 MYT
。我的代码分配了正确的时区名称 xxx
。我建议您启发创建输入数据的人以了解 proper time zone names and about ISO 8601 字符串格式。
Java 8
我的示例代码需要 Java 8,使用新的 Lambda 语法和 "streams"。
示例代码
本例进行双层排序。首先,行按小时(00、15、30、45)排序。在每个组中,行按日期时间值排序(按年、月、月日和日时排序)。
首先我们打开 .csv 文本文件,并将其内容解析为 CSVRecord
个对象。
String filePathString = "/Users/brainydeveloper/input.csv";
try {
Reader in = new FileReader( filePathString ); // Get the input file.
List<CSVRecord> recs = CSVFormat.DEFAULT.parse( in ).getRecords(); // Parse the input file.
接下来,我们将这些 CSVRecord
个对象分别包装在一个更智能的 class 中,该对象提取我们关心的两个值:首先是日期时间,其次是该日期时间的分钟数。请进一步查看 class CsvRecWithDateTimeAndMinute
.
的简单代码
List<CsvRecWithDateTimeAndMinute> smartRecs = new ArrayList<>( recs.size() ); // Collect transformed data.
for ( CSVRecord rec : recs ) { // For each CSV record…
CsvRecWithDateTimeAndMinute smartRec = new CsvRecWithDateTimeAndMinute( rec ); // …transform CSV rec into one of our objects with DateTime and minute-of-hour.
smartRecs.add( smartRec );
}
接下来,我们获取我们更智能的包装对象列表,并将该列表分成多个列表。每个新列表都包含特定时刻(00、15、30 和 45)的 CSV 行数据。我们将这些存储在地图中。
如果我们的输入数据只出现这四个值,则生成的映射将只有四个键。实际上,您可以通过查找四个以上的密钥来进行健全性检查。额外的键意味着要么在解析时出现严重错误,要么某些数据具有意外的分钟值。
每个键(这些数字的整数)都指向我们的智能包装器对象列表。这是一些奇特的新 Lambda 语法。
Map<Integer , List<CsvRecWithDateTimeAndMinute>> byMinuteOfHour = smartRecs.stream().collect( Collectors.groupingBy( CsvRecWithDateTimeAndMinute::getMinuteOfHour ) );
地图没有给我们我们的子列表,我们的键(小时整数)排序。我们可能会在获得 00
组之前取回 15
组。所以提取键,并对它们进行排序。
// Access the map by the minuteOfHour value in order. We want ":00:" first, then ":15", then ":30:", and ":45:" last.
List<Integer> minutes = new ArrayList<Integer>( byMinuteOfHour.keySet() ); // Fetch the keys of the map.
Collections.sort( minutes ); // Sort that List of keys.
按照该有序键列表,向地图询问每个键的列表。需要对该数据列表进行排序以获得我们的二级排序(按日期时间)。
List<CSVRecord> outputList = new ArrayList<>( recs.size() ); // Make an empty List in which to put our CSVRecords in double-sorted order.
for ( Integer minute : minutes ) {
List<CsvRecWithDateTimeAndMinute> list = byMinuteOfHour.get( minute );
// Secondary sort. For each group of records with ":00:" (for example), sort them by their full date-time value.
// Sort the List by defining an anonymous Comparator using new Lambda syntax in Java 8.
Collections.sort( list , ( CsvRecWithDateTimeAndMinute r1 , CsvRecWithDateTimeAndMinute r2 ) -> {
return r1.getDateTime().compareTo( r2.getDateTime() );
} );
for ( CsvRecWithDateTimeAndMinute smartRec : list ) {
outputList.add( smartRec.getCSVRecord() );
}
}
我们完成了数据处理。现在是时候导出回 CSV 格式的文本文件了。
// Now we have complete List of CSVRecord objects in double-sorted order (first by minute-of-hour, then by date-time).
// Now let's dump those back to a text file in CSV format.
try ( PrintWriter out = new PrintWriter( new BufferedWriter( new FileWriter( "/Users/brainydeveloper/output.csv" ) ) ) ) {
final CSVPrinter printer = CSVFormat.DEFAULT.print( out );
printer.printRecords( outputList );
}
} catch ( FileNotFoundException ex ) {
System.out.println( "ERROR - Exception needs to be handled." );
} catch ( IOException ex ) {
System.out.println( "ERROR - Exception needs to be handled." );
}
上面的代码一次将整个 CSV 数据集加载到内存中。如果希望节省内存,请使用 parse
方法而不是 getRecords
方法。至少那是医生似乎在说的。我还没有尝试过,因为到目前为止我的用例都很容易放入内存中。
这是包装每个 CSVRecord
对象的智能 class:
package com.example.jodatimeexperiment;
import org.apache.commons.csv.CSVRecord;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
/**
*
* @author Basil Bourque
*/
public class CsvRecWithDateTimeAndMinute
{
// Statics
static public final DateTimeFormatter FORMATTER = DateTimeFormat.forPattern( "MMM dd yyyy' - 'hh:mm:ss aa 'MYT'" ).withZone( DateTimeZone.forID( "Asia/Kuala_Lumpur" ) );
// Member vars.
private final CSVRecord rec;
private final DateTime dateTime;
private final Integer minuteOfHour;
public CsvRecWithDateTimeAndMinute( CSVRecord recordArg )
{
this.rec = recordArg;
// Parse record to extract DateTime.
// Expect value such as: Dec 15 2014 - 07:15:00 AM MYT
String input = this.rec.get( 7 - 1 ); // Index (zero-based counting). So field # 7 = index # 6.
this.dateTime = CsvRecWithDateTimeAndMinute.FORMATTER.parseDateTime( input );
// From DateTime extract minute of hour
this.minuteOfHour = this.dateTime.getMinuteOfHour();
}
public DateTime getDateTime()
{
return this.dateTime;
}
public Integer getMinuteOfHour()
{
return this.minuteOfHour;
}
public CSVRecord getCSVRecord()
{
return this.rec;
}
@Override
public String toString()
{
return "CsvRecWithDateTimeAndMinute{ " + " minuteOfHour=" + minuteOfHour + " | dateTime=" + dateTime + " | rec=" + rec + " }";
}
}
有了这个输入…
GBEP-1-2-4,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 12 月 15 日 - 07:15:00 AM MYT,+0,COMPL
GBEP-1-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 12 月 15 日 - 07:00:00 AM MYT,+0,COMPL
GBEP-2-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 12 月 15 日 - 07:30:00 AM MYT,+0,COMPL
GBEP-1-2-4,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 12 月 14 日 - 07:15:00 AM MYT,+0,COMPL
GBEP-1-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 12 月 14 日 - 07:00:00 AM MYT,+0,COMPL
GBEP-2-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 12 月 14 日 - 07:30:00 AM MYT,+0,COMPL
GBEP-1-2-4,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 1 月 22 日 - 07:15:00 AM MYT,+0,COMPL
GBEP-1-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 1 月 22 日 - 07:00:00 AM MYT,+0,COMPL
GBEP-2-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 1 月 22 日 - 07:30:00 AM MYT,+0,COMPL
…你会得到这个输出…
GBEP-1-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 1 月 22 日 - 07:00:00 AM MYT,+0,COMPL
GBEP-1-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 12 月 14 日 - 07:00:00 AM MYT,+0,COMPL
GBEP-1-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 12 月 15 日 - 07:00:00 AM MYT,+0,COMPL
GBEP-1-2-4,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 1 月 22 日 - 07:15:00 AM MYT,+0,COMPL
GBEP-1-2-4,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 12 月 14 日 - 07:15:00 AM MYT,+0,COMPL
GBEP-1-2-4,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 12 月 15 日 - 07:15:00 AM MYT,+0,COMPL
GBEP-2-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 1 月 22 日 - 07:30:00 AM MYT,+0,COMPL
GBEP-2-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 12 月 14 日 - 07:30:00 AM MYT,+0,COMPL
GBEP-2-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 12 月 15 日 - 07:30:00 AM MYT,+0,COMPL
需要根据日期列对 csv 文件进行排序。这是 masterRecords 数组列表的样子
GBEP-1-2-4,FRAG,PMTypeEthernet,NEND,TDTN,15-MIN,Dec 15 2014 - 07:15:00 AM MYT,+0,COMPL
GBEP-1-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15-MIN,Dec 15 2014 - 07:00:00 AM MYT,+0,COMPL
GBEP-2-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15-MIN,Dec 15 2014 - 07:30:00 AM MYT,+0,COMPL
我需要根据日期 07:15:00、07:30:00 等进行排序。我创建了一个代码来进行排序:
// Date is fixed on per 15min interval
ArrayList<String> sortDate = new ArrayList<String>();
sortDate.add(":00:");
sortDate.add(":15:");
sortDate.add(":30:");
sortDate.add(":45:");
BufferedWriter bw = new BufferedWriter(new FileWriter(tempPath + filename));
for (int k = 0; k < sortDate.size(); k++) {
String date = sortDate.get(k);
for (int j = 0; j < masterRecords.size(); j++) {
String[] splitLine = masterRecords.get(j).split(",", -1);
if (splitLine[10].contains(date)) {
bw.write(masterRecords.get(j) + System.getProperty("line.separator").replaceAll(String.valueOf((char) 0x0D), ""));
masterRecords.remove(j);
}
}
}
bw.close();
从上面可以看出,它将遍历第一个数组 (sortDate) 并在第二个数组(即 masterRecord)上再次循环并将其写入新文件。它似乎在整理新文件时工作,但我注意到我的 masterRecord 有 10000 条记录,但在创建新文件后记录缩小到 5000,我假设它是如何从主列表中删除记录的。有人知道为什么吗?
在循环中删除项目是不安全的。 您必须在迭代器上迭代数组,例如:
List<String> names = ....
Iterator<String> i = names.iterator();
while (i.hasNext()) {
String s = i.next(); // must be called before you can call i.remove()
// Do something
i.remove();
}
文档说:
The iterators returned by this class's iterator and listIterator methods are fail-fast: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
现在来点完全不同的东西
为了好玩,这是一种完全不同的方法。
我使用了两个库:
- 阿帕奇 Commons CSV
- Joda-Time
Apache Commons CSV
Commons CSV 库处理各种 CSV 风格的解析。它可以 return 文件中行的列表,每一行由它们的 CSVRecord
对象表示。您可以向该对象询问第一个字段、第二个字段等。
乔达时间
Joda-Time 负责解析日期时间字符串。
避免使用 3 个字母的时区代码
注意:Joda-Time 拒绝尝试解析三字母时区代码 MYT
。有充分的理由:那些 3 或 4 个字母的代码只是约定俗成,既不标准化也不唯一。我下面的示例代码假设您的所有数据都使用 MYT
。我的代码分配了正确的时区名称 xxx
。我建议您启发创建输入数据的人以了解 proper time zone names and about ISO 8601 字符串格式。
Java 8
我的示例代码需要 Java 8,使用新的 Lambda 语法和 "streams"。
示例代码
本例进行双层排序。首先,行按小时(00、15、30、45)排序。在每个组中,行按日期时间值排序(按年、月、月日和日时排序)。
首先我们打开 .csv 文本文件,并将其内容解析为 CSVRecord
个对象。
String filePathString = "/Users/brainydeveloper/input.csv";
try {
Reader in = new FileReader( filePathString ); // Get the input file.
List<CSVRecord> recs = CSVFormat.DEFAULT.parse( in ).getRecords(); // Parse the input file.
接下来,我们将这些 CSVRecord
个对象分别包装在一个更智能的 class 中,该对象提取我们关心的两个值:首先是日期时间,其次是该日期时间的分钟数。请进一步查看 class CsvRecWithDateTimeAndMinute
.
List<CsvRecWithDateTimeAndMinute> smartRecs = new ArrayList<>( recs.size() ); // Collect transformed data.
for ( CSVRecord rec : recs ) { // For each CSV record…
CsvRecWithDateTimeAndMinute smartRec = new CsvRecWithDateTimeAndMinute( rec ); // …transform CSV rec into one of our objects with DateTime and minute-of-hour.
smartRecs.add( smartRec );
}
接下来,我们获取我们更智能的包装对象列表,并将该列表分成多个列表。每个新列表都包含特定时刻(00、15、30 和 45)的 CSV 行数据。我们将这些存储在地图中。
如果我们的输入数据只出现这四个值,则生成的映射将只有四个键。实际上,您可以通过查找四个以上的密钥来进行健全性检查。额外的键意味着要么在解析时出现严重错误,要么某些数据具有意外的分钟值。
每个键(这些数字的整数)都指向我们的智能包装器对象列表。这是一些奇特的新 Lambda 语法。
Map<Integer , List<CsvRecWithDateTimeAndMinute>> byMinuteOfHour = smartRecs.stream().collect( Collectors.groupingBy( CsvRecWithDateTimeAndMinute::getMinuteOfHour ) );
地图没有给我们我们的子列表,我们的键(小时整数)排序。我们可能会在获得 00
组之前取回 15
组。所以提取键,并对它们进行排序。
// Access the map by the minuteOfHour value in order. We want ":00:" first, then ":15", then ":30:", and ":45:" last.
List<Integer> minutes = new ArrayList<Integer>( byMinuteOfHour.keySet() ); // Fetch the keys of the map.
Collections.sort( minutes ); // Sort that List of keys.
按照该有序键列表,向地图询问每个键的列表。需要对该数据列表进行排序以获得我们的二级排序(按日期时间)。
List<CSVRecord> outputList = new ArrayList<>( recs.size() ); // Make an empty List in which to put our CSVRecords in double-sorted order.
for ( Integer minute : minutes ) {
List<CsvRecWithDateTimeAndMinute> list = byMinuteOfHour.get( minute );
// Secondary sort. For each group of records with ":00:" (for example), sort them by their full date-time value.
// Sort the List by defining an anonymous Comparator using new Lambda syntax in Java 8.
Collections.sort( list , ( CsvRecWithDateTimeAndMinute r1 , CsvRecWithDateTimeAndMinute r2 ) -> {
return r1.getDateTime().compareTo( r2.getDateTime() );
} );
for ( CsvRecWithDateTimeAndMinute smartRec : list ) {
outputList.add( smartRec.getCSVRecord() );
}
}
我们完成了数据处理。现在是时候导出回 CSV 格式的文本文件了。
// Now we have complete List of CSVRecord objects in double-sorted order (first by minute-of-hour, then by date-time).
// Now let's dump those back to a text file in CSV format.
try ( PrintWriter out = new PrintWriter( new BufferedWriter( new FileWriter( "/Users/brainydeveloper/output.csv" ) ) ) ) {
final CSVPrinter printer = CSVFormat.DEFAULT.print( out );
printer.printRecords( outputList );
}
} catch ( FileNotFoundException ex ) {
System.out.println( "ERROR - Exception needs to be handled." );
} catch ( IOException ex ) {
System.out.println( "ERROR - Exception needs to be handled." );
}
上面的代码一次将整个 CSV 数据集加载到内存中。如果希望节省内存,请使用 parse
方法而不是 getRecords
方法。至少那是医生似乎在说的。我还没有尝试过,因为到目前为止我的用例都很容易放入内存中。
这是包装每个 CSVRecord
对象的智能 class:
package com.example.jodatimeexperiment;
import org.apache.commons.csv.CSVRecord;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
/**
*
* @author Basil Bourque
*/
public class CsvRecWithDateTimeAndMinute
{
// Statics
static public final DateTimeFormatter FORMATTER = DateTimeFormat.forPattern( "MMM dd yyyy' - 'hh:mm:ss aa 'MYT'" ).withZone( DateTimeZone.forID( "Asia/Kuala_Lumpur" ) );
// Member vars.
private final CSVRecord rec;
private final DateTime dateTime;
private final Integer minuteOfHour;
public CsvRecWithDateTimeAndMinute( CSVRecord recordArg )
{
this.rec = recordArg;
// Parse record to extract DateTime.
// Expect value such as: Dec 15 2014 - 07:15:00 AM MYT
String input = this.rec.get( 7 - 1 ); // Index (zero-based counting). So field # 7 = index # 6.
this.dateTime = CsvRecWithDateTimeAndMinute.FORMATTER.parseDateTime( input );
// From DateTime extract minute of hour
this.minuteOfHour = this.dateTime.getMinuteOfHour();
}
public DateTime getDateTime()
{
return this.dateTime;
}
public Integer getMinuteOfHour()
{
return this.minuteOfHour;
}
public CSVRecord getCSVRecord()
{
return this.rec;
}
@Override
public String toString()
{
return "CsvRecWithDateTimeAndMinute{ " + " minuteOfHour=" + minuteOfHour + " | dateTime=" + dateTime + " | rec=" + rec + " }";
}
}
有了这个输入…
GBEP-1-2-4,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 12 月 15 日 - 07:15:00 AM MYT,+0,COMPL GBEP-1-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 12 月 15 日 - 07:00:00 AM MYT,+0,COMPL GBEP-2-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 12 月 15 日 - 07:30:00 AM MYT,+0,COMPL GBEP-1-2-4,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 12 月 14 日 - 07:15:00 AM MYT,+0,COMPL GBEP-1-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 12 月 14 日 - 07:00:00 AM MYT,+0,COMPL GBEP-2-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 12 月 14 日 - 07:30:00 AM MYT,+0,COMPL GBEP-1-2-4,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 1 月 22 日 - 07:15:00 AM MYT,+0,COMPL GBEP-1-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 1 月 22 日 - 07:00:00 AM MYT,+0,COMPL GBEP-2-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 1 月 22 日 - 07:30:00 AM MYT,+0,COMPL
…你会得到这个输出…
GBEP-1-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 1 月 22 日 - 07:00:00 AM MYT,+0,COMPL GBEP-1-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 12 月 14 日 - 07:00:00 AM MYT,+0,COMPL GBEP-1-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 12 月 15 日 - 07:00:00 AM MYT,+0,COMPL GBEP-1-2-4,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 1 月 22 日 - 07:15:00 AM MYT,+0,COMPL GBEP-1-2-4,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 12 月 14 日 - 07:15:00 AM MYT,+0,COMPL GBEP-1-2-4,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 12 月 15 日 - 07:15:00 AM MYT,+0,COMPL GBEP-2-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 1 月 22 日 - 07:30:00 AM MYT,+0,COMPL GBEP-2-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 12 月 14 日 - 07:30:00 AM MYT,+0,COMPL GBEP-2-2-1,FRAG,PMTypeEthernet,NEND,TDTN,15 分钟,2014 年 12 月 15 日 - 07:30:00 AM MYT,+0,COMPL