Justin Leung
Justin Leung

Reputation: 39

Replace substrings using regex

So I've been trying to make this simple encryption program but I can't seem to figure out some things. The phrase I need to enter is

This is a very big morning.

When I enter it though it returns the string

This is a ag',rery dug>?/ijeb..w ssadorninjeb..w

Instead I return

This is a ajedg>P/..w',rery dg>P/ijedg>P/..w ssadorninjedg>P/..w

I don't understand why and how to fix it? I've been learning java for around a month now so I'm still fresh and if there's a similar question that's already been answered please link me there and I'll delete this post.

Here's the code:

import static java.lang.System.out;
import java.util.Scanner;
class Encryption {
    public static void main(String[] args) {
        Scanner userInput = new Scanner(System.in);
        Crypto user1 = new Crypto();
        out.print("Please enter in a sentence: ");
        String user = userInput.nextLine();
        user1.encrypt(user);
        out.print(user1.getEncrypt());
    }
}

public Crypto() { }
public String myEn;
public void encrypt(String Sentence) {
    myEn = Sentence.replaceAll("v","ag',r")
                   .replaceAll("m" , "ssad")
                   .replaceAll("g" , "jeb..w")
                   .replaceAll("b" , "dg>P/");
}

public String getEncrypt() {
        return myEn;
}
}

Upvotes: 0

Views: 169

Answers (4)

Pshemo
Pshemo

Reputation: 124215

As others already told you problem is that you are replacing characters in few iterations (replaceAll calls) instead of one. If you want to prevent replacing characters that ware used to replace other characters you can use appendReplacement and appendTail from Matcher class.

Here is how you can do it like

// We need to store somewhere info about original and its replacement
// so we will use Map
Map<String, String> replacements = new HashMap<>();
replacements.put("v", "ag',r");
replacements.put("m", "ssad");
replacements.put("g", "jeb..w");
replacements.put("b", "dg>P/");

// we need to create pattern which will find characters we want to replace
Pattern pattern = Pattern.compile("[vmgb]");//this will do 

Matcher matcher = pattern.matcher("This is a very big morning.");

StringBuffer sb = new StringBuffer();// this will be used to create
                                     // string with replaced characters

// lets start replacing process
while (matcher.find()) {//first we need to find our characters

    // then pick from map its replacement 
    String replacement = replacements.get(matcher.group());
    // and pass it to appendReplacement method
    matcher.appendReplacement(sb, replacement);

    // we repeat this process until original string has no more
    // characters to replace
}
//after all we need to append to StringBuffer part after last replacement
matcher.appendTail(sb);

System.out.println(sb);

Output:

This is a ag',rery dg>P/ijeb..w ssadorninjeb..w.

Voilà.

Upvotes: 0

Bohemian
Bohemian

Reputation: 424993

Firstly, you should use replace(), not replaceAll(). Both replace all matches found, but replaceAll() uses regex to match and replace() uses plain text.

Next, your replacements are getting in the way of each other, so use a StringBuffer and work from the eighth and end backwards replacing one character at a time.

Decryption should likewise work one character at a time starting from the left.

Upvotes: 0

Java Devil
Java Devil

Reputation: 10959

When you replace the g its replacement contains a b so then when you replace the b's you get all the b's from the g replacement also replaced. and also for the v's

What you could do is

Sentence.replaceAll("g" , "jeb..w")
    .replaceFirst("b" , "dg>P/")     // as no g's before b's and only want to replace the first
    .replaceFirst("v","ag',r")
    .replaceFirst("m" , "ssad");

But this only works for this one sentence.


What you could do:

  1. Create a map of all your characters to be replaced and there replacement.
  2. Create a list of the indices of each character that is to be replaced on the original string.
  3. Reverse the order of the list (highest to lowest)
  4. Starting from the first index in the list (the last character to be replaced) replace the character at that index with its replacement from the map
  5. Repeat 4 working backwards down the string.

Upvotes: 0

hexacyanide
hexacyanide

Reputation: 91619

The reason you're getting the different output is because the chained replaces take the return value of the previous replaces. So in your case, if there was a v, it would be changed with ag',r which has a g in it. That g would then trigger replaceAll("g" , "jeb..w").

To avoid this from happening, you should change the order of the replaces:

Sentence.replaceAll("g" , "jeb..w")
        .replaceAll("b" , "dg>P/")
        .replaceAll("v","ag',r")
        .replaceAll("m" , "ssad");

However, the first two replace statements can't be fixed because one replaces b with a string that has a g in it, and vice-versa, so you might want to change the characters you're replacing.

Upvotes: 2

Related Questions