Java String replace substring by substring

Description

Java String replace substring by substring

public class Main {
  public static void main(String[] argv) throws Exception {
    String text = "CSS CSS HTML HTML Java Java";
    String[] searchList = new String[] { "CSS", "HTML", "Java" };
    String[] replacementList = new String[] { "css", "html", "javascript" };
    System.out.println(replaceEachRepeatedly(text, searchList, replacementList));
  }//from www .j a v a  2 s . c  o  m

  /**
   * <p>
   * Replaces all occurrences of Strings within another String.
   * </p>
   * 
   * <p>
   * A <code>null</code> reference passed to this method is a no-op, or if any
   * "search string" or "string to replace" is null, that replace will be ignored.
   * This will not repeat. For repeating replaces, call the overloaded method.
   * </p>
   * 
   * <pre>
   *  replaceEach(null, *, *, *) = null
   *  replaceEach("", *, *, *) = ""
   *  replaceEach("aba", null, null, *) = "aba"
   *  replaceEach("aba", new String[0], null, *) = "aba"
   *  replaceEach("aba", null, new String[0], *) = "aba"
   *  replaceEach("aba", new String[]{"a"}, null, *) = "aba"
   *  replaceEach("aba", new String[]{"a"}, new String[]{""}, *) = "b"
   *  replaceEach("aba", new String[]{null}, new String[]{"a"}, *) = "aba"
   *  replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}, *) = "wcte"
   *  (example of how it repeats)
   *  replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, false) = "dcte"
   *  replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, true) = "tcte"
   *  replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, true) = IllegalArgumentException
   *  replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, false) = "dcabe"
   * </pre>
   * 
   * @param text
   *          text to search and replace in, no-op if null
   * @param searchList
   *          the Strings to search for, no-op if null
   * @param replacementList
   *          the Strings to replace them with, no-op if null
   * @return the text with any replacements processed, <code>null</code> if null
   *         String input
   * @throws IllegalArgumentException
   *           if the search is repeating and there is an endless loop due to
   *           outputs of one being inputs to another
   * @throws IndexOutOfBoundsException
   *           if the lengths of the arrays are not the same (null is ok, and/or
   *           size 0)
   * @since 2.4
   */
  public static String replaceEachRepeatedly(String text, String[] searchList, String[] replacementList) {
    // timeToLive should be 0 if not used or nothing to replace, else it's
    // the length of the replace array
    int timeToLive = searchList == null ? 0 : searchList.length;
    return replaceEach(text, searchList, replacementList, true, timeToLive);
  }
  /**
   * <pre>
   *  replaceEach(null, *, *)        = null
   *  replaceEach("", *, *)          = ""
   *  replaceEach("aba", null, null) = "aba"
   *  replaceEach("aba", new String[0], null) = "aba"
   *  replaceEach("aba", null, new String[0]) = "aba"
   *  replaceEach("aba", new String[]{"a"}, null)  = "aba"
   *  replaceEach("aba", new String[]{"a"}, new String[]{""})  = "b"
   *  replaceEach("aba", new String[]{null}, new String[]{"a"})  = "aba"
   *  replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"})  = "wcte"
   *  (example of how it does not repeat)
   *  replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"})  = "dcte"
   * </pre>
   */
  public static String replaceEach(String text, String[] searchList, String[] replacementList) {
    return replaceEach(text, searchList, replacementList, false, 0);
  }

  /**
   * <pre>
   *  replaceEach(null, *, *, *) = null
   *  replaceEach("", *, *, *) = ""
   *  replaceEach("aba", null, null, *) = "aba"
   *  replaceEach("aba", new String[0], null, *) = "aba"
   *  replaceEach("aba", null, new String[0], *) = "aba"
   *  replaceEach("aba", new String[]{"a"}, null, *) = "aba"
   *  replaceEach("aba", new String[]{"a"}, new String[]{""}, *) = "b"
   *  replaceEach("aba", new String[]{null}, new String[]{"a"}, *) = "aba"
   *  replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}, *) = "wcte"
   *  (example of how it repeats)
   *  replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, false) = "dcte"
   *  replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, true) = "tcte"
   *  replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, *) = IllegalArgumentException
   * </pre>
   */
  private static String replaceEach(String text, String[] searchList, String[] replacementList, boolean repeat,
      int timeToLive) {

    // mchyzer Performance note: This creates very few new objects (one major goal)
    // let me know if there are performance requests, we can create a harness to
    // measure

    if (text == null || text.length() == 0 || searchList == null || searchList.length == 0 || replacementList == null
        || replacementList.length == 0) {
      return text;
    }

    // if recursing, this shouldnt be less than 0
    if (timeToLive < 0) {
      throw new IllegalStateException("TimeToLive of " + timeToLive + " is less than 0: " + text);
    }

    int searchLength = searchList.length;
    int replacementLength = replacementList.length;

    // make sure lengths are ok, these need to be equal
    if (searchLength != replacementLength) {
      throw new IllegalArgumentException(
          "Search and Replace array lengths don't match: " + searchLength + " vs " + replacementLength);
    }

    // keep track of which still have matches
    boolean[] noMoreMatchesForReplIndex = new boolean[searchLength];

    // index on index that the match was found
    int textIndex = -1;
    int replaceIndex = -1;
    int tempIndex = -1;

    // index of replace array that will replace the search string found
    // NOTE: logic duplicated below START
    for (int i = 0; i < searchLength; i++) {
      if (noMoreMatchesForReplIndex[i] || searchList[i] == null || searchList[i].length() == 0
          || replacementList[i] == null) {
        continue;
      }
      tempIndex = text.indexOf(searchList[i]);

      // see if we need to keep searching for this
      if (tempIndex == -1) {
        noMoreMatchesForReplIndex[i] = true;
      } else {
        if (textIndex == -1 || tempIndex < textIndex) {
          textIndex = tempIndex;
          replaceIndex = i;
        }
      }
    }
    // NOTE: logic mostly below END

    // no search strings found, we are done
    if (textIndex == -1) {
      return text;
    }

    int start = 0;

    // get a good guess on the size of the result buffer so it doesnt have to double
    // if it goes over a bit
    int increase = 0;

    // count the replacement text elements that are larger than their corresponding
    // text being replaced
    for (int i = 0; i < searchList.length; i++) {
      int greater = replacementList[i].length() - searchList[i].length();
      if (greater > 0) {
        increase += 3 * greater; // assume 3 matches
      }
    }
    // have upper-bound at 20% increase, then let Java take over
    increase = Math.min(increase, text.length() / 5);

    StringBuffer buf = new StringBuffer(text.length() + increase);

    while (textIndex != -1) {

      for (int i = start; i < textIndex; i++) {
        buf.append(text.charAt(i));
      }
      buf.append(replacementList[replaceIndex]);

      start = textIndex + searchList[replaceIndex].length();

      textIndex = -1;
      replaceIndex = -1;
      tempIndex = -1;
      // find the next earliest match
      // NOTE: logic mostly duplicated above START
      for (int i = 0; i < searchLength; i++) {
        if (noMoreMatchesForReplIndex[i] || searchList[i] == null || searchList[i].length() == 0
            || replacementList[i] == null) {
          continue;
        }
        tempIndex = text.indexOf(searchList[i], start);

        // see if we need to keep searching for this
        if (tempIndex == -1) {
          noMoreMatchesForReplIndex[i] = true;
        } else {
          if (textIndex == -1 || tempIndex < textIndex) {
            textIndex = tempIndex;
            replaceIndex = i;
          }
        }
      }
      // NOTE: logic duplicated above END

    }
    int textLength = text.length();
    for (int i = start; i < textLength; i++) {
      buf.append(text.charAt(i));
    }
    String result = buf.toString();
    if (!repeat) {
      return result;
    }

    return replaceEach(result, searchList, replacementList, repeat, timeToLive - 1);
  }

}

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */



PreviousNext

Related