2

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();
            }
        }
    }
}
8
  • 1
    What have you tried? Could you show your code? Commented Sep 22, 2021 at 8:58
  • 1
    So what are you having trouble with? The file-reading? The sorting? Can you show your current approach? Commented Sep 22, 2021 at 8:58
  • Can you add what the output file looks like? Commented Sep 22, 2021 at 9:02
  • 1
    The extra step where you create an additional String[] can be skipped. Simply iterate over your rows list. Commented Sep 22, 2021 at 9:06
  • 1
    Also, you're likely sorting alphanumerically - add "1°", "5°" and "100°" to your input file and see if you get the expected results. And look at stackoverflow.com/a/1694770/13447 for reversing the order. Commented Sep 22, 2021 at 9:08

4 Answers 4

1

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

Sign up to request clarification or add additional context in comments.

Comments

1

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()));
    }

}

1 Comment

Good answer! I would use parseDouble to avoid an exception if some temperatures have a fractional part.
1

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;
    });

1 Comment

Very good suggestion: Ideally use a class to hold the line's content and the numerical value.
1

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 suffix needed to replace and append
    String suffix = "° C";
    // 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> sortedTemperatures = Files.readAllLines(temperaturesPath)
                                                // stream them
                                                .stream()
                                                // map the temperature values to Integers
                                                .map(line -> Integer.parseInt(line.replace(suffix, "")))
                                                // reverse-sort them
                                                .sorted(Comparator.reverseOrder())
                                                // map the sorted Integers back to a String with the suffix
                                                .map(tempVal -> String.format("%d%s", tempVal, suffix))
                                                // and collect the resultin Strings in a List
                                                .collect(Collectors.toList());
                // finally print the elements of the result list
                sortedTemperatures.forEach(System.out::println);
            }
        }
    }
}

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

1 Comment

Very good point, @ArvindKumarAvinash... Changed implementation accordingly, was a bad approach anyway.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.