使用Java流从csv文件中筛选



我有一个csv文件,里面有来自SW的字符,我想用java流找到最重的字符。以下是文件的示例:

name;height;mass;hair_color;skin_color;eye_color;birth_year;gender
Luke Skywalker;172;77;blond;fair;blue;19BBY;male
C-3PO;167;75;n/a;gold;yellow;112BBY;n/a
R2-D2;96;32;n/a;white, blue;red;33BBY;n/a
Darth Vader;202;136;none;white;yellow;41.9BBY;male
Leia Organa;150;49;brown;light;brown;19BBY;female
Owen Lars;178;120;brown, grey;light;blue;52BBY;male
Beru Whitesun lars;165;75;brown;light;blue;47BBY;female
Grievous;216;159;none;brown, white;green, yellow;unknown;male
Finn;unknown;unknown;black;dark;dark;unknown;male
Rey;unknown;unknown;brown;light;hazel;unknown;female
Poe Dameron;unknown;unknown;brown;light;brown;unknown;male

期望的输出是字符串";悲伤";。

最初,我想创建一个Character类,在那里我可以存储数据,并在分割行后使用对象而不是String数组。然而,每个值都可能是未知的或n/a,所以不太确定如何处理它。有没有办法只使用流来实现这一点?

这是我的第一次尝试,将每一行映射到具有字段nameheight的新Person对象,但是这种方法不能正确处理未知输入。

public static String getHeaviestCharacter(String file) throws IOException {
return Files.lines(Paths.get(file))
.map(line -> line.split(";"))
.map(part -> new Person(part[0], part[2]))
.max((p1, p2) -> Integer.compare(p1.getWeight(), p2.getWeight()))
.map(p1.getName());
}

正如其他人所指出的,我怀疑流是解决特定问题的最佳方法。但既然你问了,只是为了好玩,我就试一试。经过多次网络搜索和反复尝试,我似乎找到了一个使用流的解决方案。

我们使用NIO.2类Path&Files打开数据文件。

我们通过调用Files.lines来定义流。

我们通过调用Stream#skip来省略标题行。

您的一些输入行具有非数值";未知";在我们的目标第三个领域。所以我们调用Stream#filter来忽略这些行。我们使用String#split提取第三个字段,同时传递令人讨厌的基于零的索引号2

为了获得第三列中的最高数字,我们需要排序。为了排序,我们提取通过Comparator.comparingInt创建的Comparator中的第三个字段。为了获得所需的int值,我们使用Integer.parseInt解析第三个字段的文本。

排序后,我们需要访问流中的最后一个元素,因为它应该具有最大权重的字符。这对我来说似乎很笨拙,但显然获得流的最后一个元素的方法是.reduce( ( first , second ) -> second ).orElse( null )。我真希望我们有一个Stream#last方法!

最后一个元素是String对象,它是输入文件中的一行文本。因此,我们需要再次拆分字符串。但这一次当我们分开时,我们使用第一个元素,而不是第三个,因为我们的目标是报告角色的名称。第一个元素由令人讨厌的CCD_ 18的基于零的索引号来标识。

瞧,我们得到了Grievous作为我们的最终结果。

Path path = Paths.get( "/Users/basil_dot_work/inputs.csv" );
if ( Files.notExists( path ) ) { throw new IllegalStateException( "Failed to find file at path: " + path ); }
Stream < String > lines;
try { lines = Files.lines( path , StandardCharsets.UTF_8 ); } catch ( IOException e ) { throw new RuntimeException( e ); }
String result =
lines
.skip( 1L )  // Skip the header row, with column names.
.filter(  // Filter out lines whose targeted value is "unknown". We need text made up only of digits.
line -> ! line.split( ";" )[ 2 ].equalsIgnoreCase( "unknown" )
)
.sorted(  // Sort by extracting third field’s text, then parse to get an `int` value.
Comparator.comparingInt( ( String line ) -> Integer.parseInt( line.split( ";" )[ 2 ] ) )
)
.reduce( ( first , second ) -> second ).orElse( null ) // Get last element.
.split( ";" )[ 0 ]; // Extract name of character from first field of our one and only line of input left remaining after processing.
System.out.println( "result = " + result );

result=糟糕的

请务必将我的方法与Florian Hartung的另一本《答案》进行比较。另一种可能会更好;我还没有仔细学习。

没有流

相比之下,这里是更传统的代码,很少或根本不使用流。

我们以与上述相同的方式读取文件中的行。

我们需要跳过第一行,列标题的标题行。但是Files.lines返回的List对象是不可修改的。因此,我们不能简单地删除该列表的第一个元素。因此,我们通过调用lines.subList( 1 , lines.size() )有效地跳过了第一行。subList命令返回一个列表,该列表作为视图映射回原始列表,而不是实际创建一个新的单独列表。这是有效的,适合我们在这里使用。

我们将类定义为一个记录,用于保存每个人的详细信息。我们使用Integer而不是int,这样我们就可以为携带unknown文本而不是数字的行保留null

对于每一行,我们直接将文本项转移到String成员字段。但对于高度和质量,我们使用三元运算符来返回null或实例化Integer对象。

我们通过添加到列表中来收集Person对象。

要获得mass最大的Person对象的最大值,我们需要忽略那些具有null的对象。因此,我们在这里使用一个简单的流来制作具有非null质量的Person对象的新列表。这个流可以用传统的循环代替,但会更详细。

使用我们的过滤列表,我们在传递比较mass成员字段的Comparator对象时调用Collections.max

我们最终得到一个Person对象。因此,我们对其name成员域进行了查询。

瞧,我们得到了Grievous作为我们的最终结果。

Path path = Paths.get( "/Users/basil_dot_work/inputs.csv" );
if ( Files.notExists( path ) ) { throw new IllegalStateException( "Failed to find file at path: " + path ); }
List < String > lines;
try { lines = Files.lines( path , StandardCharsets.UTF_8 ).toList(); } catch ( IOException e ) { throw new RuntimeException( e ); }
lines = lines.subList( 1 , lines.size() ); // Skip over first line.
record Person( String name , Integer height , Integer mass , String hair_color , String skin_color , String eye_color , String birth_year , String gender ) { }
List < Person > persons = new ArrayList <>();
for ( String line : lines )
{
String[] parts = line.split( ";" );
Integer height = ( parts[ 1 ].equalsIgnoreCase( "unknown" ) ) ? null : Integer.valueOf( parts[ 1 ] );
Integer mass = ( parts[ 2 ].equalsIgnoreCase( "unknown" ) ) ? null : Integer.valueOf( parts[ 2 ] );
Person person = new Person( parts[ 0 ] , height , mass , parts[ 3 ] , parts[ 4 ] , parts[ 5 ] , parts[ 6 ] , parts[ 7 ] );
persons.add( person );
}
System.out.println( "persons = " + persons );
List < Person > personsWithMass = persons.stream().filter( person -> Objects.nonNull( person.mass ) ).toList();
Person heaviestPerson = Collections.max( personsWithMass , Comparator.comparing( person -> person.mass ) );
System.out.println( "heaviest Person’s name = " + heaviestPerson.name );

最重的人的名字=悲伤的

我不建议使用Streams,而是使用一些CSV库,因为它更安全。


public static void main(String[] args) {
try {
BufferedReader reader = new BufferedReader(new FileReader(new File("characters.csv")));
// Skip first line
reader.readLine();
Optional<String> optionalHeaviestCharacter = getHeaviestCharactersName(reader.lines());
System.out.println(optionalHeaviestCharacter);
} catch (IOException e) {
e.printStackTrace();
}
}
public static Optional<String> getHeaviestCharactersName(Stream<String> lineStream) {
return lineStream
.map(lineString -> lineString.split(";")) // map every line string to an array with all values
.filter(values -> values[2].matches("[0-9]+")) // filter out characters with a non-number value as a mass
.max((values1, values2) -> Integer.compare(Integer.parseInt(values1[2]), Integer.parseInt(values2[2]))) // get element with maximum mass
.map(heaviestValues -> heaviestValues[0]); // map values array of heaviest character to its name
}

首先我们读取文件,我将其命名为characters.csv。您可能需要编辑文件路径以指向您的文件。

BufferedReader reader = new BufferedReader(new FileReader(new File("characters.csv")));

然后,我们通过调用reader.lines()方法读取文件中的所有行,每行都是Stream<String>中的String

函数CCD_ 45随后将返回一个CCD_。例如,当所有字符都具有未知/无效的质量或根本不存在字符时,"可选"将为空。

如果你认为总会有至少一个字符具有有效的质量,那么你只需要得到最重的字符的名称optionalHeaviestCharacter.get()。否则,您必须首先检查可选项是否为空:

if (optionalHeaviestCharacter.isEmpty()) {
System.out.println("Could not find a character with the heaviest mass");
} else {
System.out.println("Heaviest character is " + optionalHeaviestCharacter.get());
}

您只需拨打即可获得姓名

相关内容

  • 没有找到相关文章

最新更新