I have tried multiple solutions suggested by other threads on StackOverflow, but I haven't found a solution. I'm trying to read a .txt file and sort the content from largest to smallest, using Java. This is what my .txt file contains:
16° C
15° C
18° C
13° C
17° C
19° C
21° C
20° C
16° C
Here is what I've tried:
import java.io.*;
import java.util.*;
public class temperatur {
public static void main(String[] args) throws IOException {
BufferedReader reader = null;
PrintWriter outputStream = null;
ArrayList<String> rows = new ArrayList<String>();
try {
reader = new BufferedReader(new FileReader("temp.txt"));
outputStream = new PrintWriter(new FileWriter("tempout.txt"));
String file;
while ((file = reader .readLine()) != null) {
rows.add(file);
}
Collections.sort(rows);
String[] strArr= rows.toArray(new String[0]);
for (String cur : strArr)
outputStream.println(cur);
} finally {
if (reader != null) {
inputStream.close();
}
if (outputStream != null) {
outputStream.close();
}
}
}
}
CodePudding user response:
This reads through temp.text
and splits each line to get the numeric part (given that it is always before the °
symbol) to add into an tempList
which is then sorted. The sorted list is then written into the output file by joining it with the rest of the string as given in the input file.
import java.io.*;
import java.util.*;
public class temperatur{
public static void main(String[] args) throws IOException {
File file = new File("temp.txt");
List<Double> tempList = new ArrayList();
BufferedReader br = new BufferedReader(new FileReader(file));
String st;
while ((st = br.readLine()) != null) {
String[] temp = st.split("°");
tempList.add(Double.parseDouble(temp[0]));
}
Collections.sort(tempList);
Collections.reverse(tempList);
//Write into a new file
FileWriter file2 = new FileWriter("tempout.txt");
String sb = "";
for (double a : tempList) {
sb = a "°" " C\n";
}
file2.write(sb);
file2.close();
}
}
tempout.txt
21.0° C
20.0° C
19.0° C
18.0° C
17.0° C
16.0° C
16.0° C
15.0° C
13.0° C
CodePudding user response:
Here's an example that uses newer technologies like java.nio
instead of java.io
for file system operations as well as streaming data:
public static void main(String[] args) throws IOException {
// define the path to the file as String (use your path!)
String temperaturesFile = "/path/to/temperatures.txt";
// create a java.nio.Path from it
Path temperaturesPath = Paths.get(temperaturesFile);
// check if it exists
if (Files.exists(temperaturesPath, LinkOption.NOFOLLOW_LINKS)) {
// then check if it actually is a regular file
if (Files.isRegularFile(temperaturesPath, LinkOption.NOFOLLOW_LINKS)) {
// and check if the file is readable
if (Files.isReadable(temperaturesPath)) {
// then read all its lines
List<String> sortedLines = Files.readAllLines(temperaturesPath)
// stream them
.stream()
// reverse-sort them
.sorted(Comparator.reverseOrder())
// and collect the result in a List
.collect(Collectors.toList());
// finally print the elements of the result list
sortedLines.forEach(System.out::println);
}
}
}
}
Attention please
This code will work well for the file example you posted because that file neither contains negative temperature values (most likely, there will be such if temperature is measured in Celsius) nor does it contain temperature values above 99 (less likely in Celsius, admittedly).
This example compares String
s the lexicographical way, which is ok for the numbers provided as example, but would crash/fail if one of the above mentioned variations comes into play. In order to tackle those, you would have to provide conversion from plain String
to a class TemperatureCelsius
at best. Maybe extracting the numerical value (including the sign) would be sufficient for a correct sorting order.
However, the result of executing this code with a valid file in a valid path is:
21° C
20° C
19° C
18° C
17° C
16° C
16° C
15° C
13° C
CodePudding user response:
You should parse your data, e.g.
static Pattern temperatureRegex = Pattern.compile("^([0-9] )° C$");
private static int parseTemperature(String s) {
return of(temperatureRegex.matcher(s)) // get a matcher
.filter(Matcher::find) // must find
.map(m -> parseInt(m.group(1))) // to integer
.orElseThrow(); // or exception
}
then, simply read and writes lines mapping with sort
write(Paths.get("/home/josejuan/tmp/Tout.txt"),
readAllLines(Paths.get("/home/josejuan/tmp/T.txt")).stream()
.sorted(Comparator.comparingInt(SortFile::parseTemperature)).collect(toList()));
(Aside full code)
package com.computermind.sandbox.fileprocessing;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static java.lang.Integer.parseInt;
import static java.nio.file.Files.readAllLines;
import static java.nio.file.Files.write;
import static java.util.Optional.of;
import static java.util.stream.Collectors.toList;
public class SortFile {
static Pattern temperatureRegex = Pattern.compile("^([0-9] )° C$");
private static int parseTemperature(String s) {
return of(temperatureRegex.matcher(s)) // get a matcher
.filter(Matcher::find) // must find
.map(m -> parseInt(m.group(1))) // to integer
.orElseThrow(); // or exception
}
public static void main(String... args) throws IOException {
write(Paths.get("/home/josejuan/tmp/Tout.txt"),
readAllLines(Paths.get("/home/josejuan/tmp/T.txt")).stream()
.sorted(Comparator.comparingInt(SortFile::parseTemperature)).collect(toList()));
}
}
CodePudding user response:
Expanding on my comment, here are examples of how to sort the lines you already have in rows
. This is assuming you want to keep the original lines instead of reconstructing them.
parse and sort
Ideally use a class to hold the line's content and the numerical value, e.g.:
//simplified, add setters, getters etc.
class Line {
String content;
double temp;
}
And build it when reading the lines:
List<Line> rows = ...
String lineContent;
while ((lineContent= reader .readLine()) != null) {
double temp = Double.parseDouble(lineContent.split("°")[0]);
rows.add(new Line(lineContent, temp));
}
Then sort:
Collections.sort(rows, Comparator.comparing(Line::getTemp).reversed());
Sort the strings directly
Strings are sorted by comparing characters and thus "10"
would be considered smaller than "2"
. Thus we need to use a "trick":
- Sort by length first so 2 is smaller than 10
- Then sort lexically
Collections.sort(rows, Comparator.comparing(String::length)
.thenComparing(Function.identity())
.reversed());
Note that this breaks if numbers can get negative or have a different format (i.e. fractions of differing lengths, different number of spaces, additional data in the lines etc.). The compators would need to be more complex in those cases and the higher the complexity the easier it would be to use the "parse and sort" approach.
For your reference, here's a comparator that should be able to sort strings of the same format (same number of fraction digits, etc.) supporting signs ( and -):
Collections.sort(rows, (String left, String right) -> {
char c1Left = left.charAt(0);
char c1Right = right.charAt(0);
int direction = -1; //1 for ascending
//negative numbers are smaller than 0 or positive numbers
int result = Integer.compare(c1Left == '-' ? -1 : 0, c1Right == '-' ? -1 : 0);
if( result != 0) {
return result * direction;
}
//at this point, both numbers are either both positive or both negative
//for negative numbers we need to reserve direction since we'll only compare "absolute values"
if( c1Right == '-' ) {
direction *= -1;
}
String subLeft = Character.isDigit(c1Left) ? left : left.substring(1);
String subRight = Character.isDigit(c1Right) ? right : right.substring(1);
//shorter numbers are smaller than longer numbers (takes care of cases like 2 vs 10 or 3.1 vs 21.0)
result = Integer.compare(subLeft.length(), subRight.length());
if( result != 0) {
return result * direction;
}
//sign and length are equal, the rest is a simple character comparison
result = subLeft.compareTo(subRight);
return result * direction;
});