Search code examples
javadesign-patternsreversematcher

Java - Reversing words in a string


This is our code:

import java.io.File;
import java.io.FileNotFoundException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class reverse {

public static void main(String[] args) throws FileNotFoundException {
    File fil = new File("textFile.txt");
    Scanner scan = new Scanner(fil);

    while (scan.hasNext()) {
        String in = scan.nextLine();
        in = new StringBuffer(in).reverse().toString();
        Pattern replace = Pattern.compile("\\W+");
        Matcher matcher = replace.matcher(in);
        System.out.println(matcher.replaceAll("\t"));
        }



    }

}

In our textFile.txt we have letters, numbers and words in different lines. We want print them out in reverse order. So if one line is: Yes, 4 Chocolate milk. We want to print out: milk Chocolate 4 Yes.

Our code prints out words in reverse, so yes becomes sey. We don't want it to be like this, but don't know how to change it. We've been thinking about creating a stack, but we don't how it'll work together with out Pattern and Matcher stuff.


Solution

  • You shouldn't reverse the line you read but split the line into a collection and reverse that. What you do is reversing the characters, so you get this:

    Yes, 4 Chocolate milk -> klim etalocohC 4 , seY
    

    If you split the line into ["Yes","4","Chocolate milk"] you can reverse that array/list.

    The problem is: I guess you want to keep Chocolate milk in that order, so you'd need to define what words belong together. If your input is always <words>, <number> <words> you could split on , first, to separate the first <words> and then on the first whitespace of the remainder in order to split the number and the second <words>.

    Update: try this:

    String input = "Yes sir, 4 Chocolate milk";
    
    //Pattern is: 
    //- one or more words separated by whitespace as group 1: ((?:\\w+\\s*)+)
    //- a comma 
    //- optional whitespace: \\s*
    //- an integer number as group 2: (\\d+)
    //- optional whitespace \\s*
    //- an arbitrary rest (which might contain anything) as group 3: (.*)
    Pattern p = Pattern.compile( "((?:\\w+\\s*)+),\\s*(\\d+)\\s*(.*)" );
    Matcher m = p.matcher( input );
    
    List<String> list = new ArrayList<String>();
    while(m.find()) {
      list.add( m.group( 1 ) );
      list.add( m.group( 2 ) );
      list.add( m.group( 3 ) );
    }
    
    Collections.reverse( list );
    for( String s : list) {
      System.out.println(s);
    }
    

    The output should be

    Chocolate milk
    4
    Yes sir
    

    Note that this depends on the layout of your input and is just meant to provide a start. You'd also need to integrate that with your other pattern matching logic.