IT박스

한 줄이 다른 줄을 바꾸지 않는 방법으로 두 줄을 어떻게 바꿀 수 있습니까?

itboxs 2020. 6. 3. 21:45
반응형

한 줄이 다른 줄을 바꾸지 않는 방법으로 두 줄을 어떻게 바꿀 수 있습니까?


다음 코드가 있다고 가정 해 봅시다.

String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar."
story = story.replace("foo", word1);
story = story.replace("bar", word2);

이 코드를 실행 한 후,의 값이 story될 것입니다"Once upon a time, there was a foo and a foo."

반대 순서로 교체하면 비슷한 문제가 발생합니다.

String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar."
story = story.replace("bar", word2);
story = story.replace("foo", word1);

의 값이 story됩니다"Once upon a time, there was a bar and a bar."

내 목표는 설정하는 것입니다 story으로 "Once upon a time, there was a bar and a foo."나는 그것을 달성 할 수 있는가?


Apache Commons StringUtilsreplaceEach()메소드를 사용하십시오 .

StringUtils.replaceEach(story, new String[]{"foo", "bar"}, new String[]{"bar", "foo"})

문장에 아직없는 중간 값을 사용합니다.

story = story.replace("foo", "lala");
story = story.replace("bar", "foo");
story = story.replace("lala", "bar");

비판에 대한 응답으로 : zq515sqdqs5d5sq1dqs4d1q5dqqé "& é5d4sqjshsjddjhodfqsqc, nvùq ^ µù; d & € sdq : d :;) àçàçlala같이 충분히 드문 문자열을 사용하는 경우 에는 아무 말도 하지 않습니다 사용자가 입력 할 것인지 알 수있는 유일한 방법은 소스 코드를 알고 그 시점에서 완전히 다른 수준의 걱정을하는 것입니다.

예, 아마도 멋진 정규식 방법이있을 수 있습니다. 나는 읽을 수있는 것을 선호한다.

또한 의견@David Conrad가 제공 한 훌륭한 조언을 반복합니다 .

가능성이없는 것으로 현명하게 (멍청하게) 선택된 문자열을 사용하지 마십시오. 유니 코드 개인 사용 영역 (U + E000..U + F8FF)의 문자를 사용하십시오. 이러한 문자는 합법적으로 입력에 포함되지 않아야하기 때문에 먼저 제거하고 (일부 응용 프로그램 내에서 응용 프로그램 특정 의미 만 있음) 교체 할 때 자리 표시 자로 사용하십시오.


Matcher#appendReplacementand를 사용하여 이와 같은 것을 시도 할 수 있습니다 Matcher#appendTail.

String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar.";

Pattern p = Pattern.compile("foo|bar");
Matcher m = p.matcher(story);
StringBuffer sb = new StringBuffer();
while (m.find()) {
    /* do the swap... */
    switch (m.group()) {
    case "foo":
        m.appendReplacement(sb, word1);
        break;
    case "bar":
        m.appendReplacement(sb, word2);
        break;
    default:
        /* error */
        break;
    }
}
m.appendTail(sb);

System.out.println(sb.toString());
옛날 옛적에 술집과 foo가있었습니다.

이것은 쉬운 문제가 아닙니다. 검색 대체 매개 변수가 많을수록 더 까다로워집니다. 추악하고 우아하고 효율적으로 낭비되는 팔레트에 여러 가지 옵션이 있습니다.

  • @AlanHay가 권장 되는대로StringUtils.replaceEach Apache Commons에서 사용하십시오 . 프로젝트에 새로운 의존성을 추가 할 수 있다면 좋은 옵션입니다. 운이 좋을 수도 있습니다 : 종속성이 이미 프로젝트에 포함되었을 수 있습니다

  • @Jeroen이 제안한 대로 임시 자리 표시자를 사용하고 2 단계로 교체를 수행하십시오.

    1. 모든 검색 패턴을 원본 텍스트에없는 고유 한 태그로 바꿉니다.
    2. 자리 표시자를 실제 대상 교체로 교체

    이는 여러 가지 이유로 큰 접근 방식이 아닙니다. 첫 번째 단계에서 사용 된 태그가 실제로 고유한지 확인해야합니다. 실제로 필요한 것보다 더 많은 문자열 교체 작업을 수행합니다.

  • 모든 패턴에서 정규식을 구축하고 함께 방법을 사용 Matcher하고StringBuffer 등이 제안 @arshajii . 이 끔찍한되지 않습니다,하지만 그 큰 중 하나, 정규식을 구축하는 것은 일종의 hackish의, 그리고이 포함로 StringBuffer찬성 얼마 전 패션 A의 나갔다한다 StringBuilder.

  • @mjolka가 제안한 재귀 솔루션을 사용 하여 일치하는 패턴으로 문자열을 분할하고 나머지 세그먼트를 반복하십시오 . 이것은 작고 매우 우아한 훌륭한 솔루션입니다. 약점은 잠재적으로 많은 부분 문자열 및 연결 작업이며 모든 재귀 솔루션에 적용되는 스택 크기 제한입니다.

  • @msandiford가 제안한 대로 텍스트를 단어로 나누고 Java 8 스트림을 사용하여 교체를 우아하게 수행 하지만 단어 경계에서 분할해도 괜찮은 경우에만 작동하므로 일반적인 솔루션으로는 적합하지 않습니다.

다음은 Apache 구현 에서 빌린 아이디어를 기반으로 한 내 버전 입니다. 단순하거나 우아하지는 않지만 작동하지만 불필요한 단계없이 비교적 효율적이어야합니다. 간단히 말해서, 그것은 다음과 같이 작동합니다 : 텍스트에서 다음으로 일치하는 검색 패턴을 반복적으로 찾고 a StringBuilder사용하여 일치하지 않는 세그먼트와 대체물을 누적하십시오.

public static String replaceEach(String text, String[] searchList, String[] replacementList) {
    // TODO: throw new IllegalArgumentException() if any param doesn't make sense
    //validateParams(text, searchList, replacementList);

    SearchTracker tracker = new SearchTracker(text, searchList, replacementList);
    if (!tracker.hasNextMatch(0)) {
        return text;
    }

    StringBuilder buf = new StringBuilder(text.length() * 2);
    int start = 0;

    do {
        SearchTracker.MatchInfo matchInfo = tracker.matchInfo;
        int textIndex = matchInfo.textIndex;
        String pattern = matchInfo.pattern;
        String replacement = matchInfo.replacement;

        buf.append(text.substring(start, textIndex));
        buf.append(replacement);

        start = textIndex + pattern.length();
    } while (tracker.hasNextMatch(start));

    return buf.append(text.substring(start)).toString();
}

private static class SearchTracker {

    private final String text;

    private final Map<String, String> patternToReplacement = new HashMap<>();
    private final Set<String> pendingPatterns = new HashSet<>();

    private MatchInfo matchInfo = null;

    private static class MatchInfo {
        private final String pattern;
        private final String replacement;
        private final int textIndex;

        private MatchInfo(String pattern, String replacement, int textIndex) {
            this.pattern = pattern;
            this.replacement = replacement;
            this.textIndex = textIndex;
        }
    }

    private SearchTracker(String text, String[] searchList, String[] replacementList) {
        this.text = text;
        for (int i = 0; i < searchList.length; ++i) {
            String pattern = searchList[i];
            patternToReplacement.put(pattern, replacementList[i]);
            pendingPatterns.add(pattern);
        }
    }

    boolean hasNextMatch(int start) {
        int textIndex = -1;
        String nextPattern = null;

        for (String pattern : new ArrayList<>(pendingPatterns)) {
            int matchIndex = text.indexOf(pattern, start);
            if (matchIndex == -1) {
                pendingPatterns.remove(pattern);
            } else {
                if (textIndex == -1 || matchIndex < textIndex) {
                    textIndex = matchIndex;
                    nextPattern = pattern;
                }
            }
        }

        if (nextPattern != null) {
            matchInfo = new MatchInfo(nextPattern, patternToReplacement.get(nextPattern), textIndex);
            return true;
        }
        return false;
    }
}

단위 테스트 :

@Test
public void testSingleExact() {
    assertEquals("bar", StringUtils.replaceEach("foo", new String[]{"foo"}, new String[]{"bar"}));
}

@Test
public void testReplaceTwice() {
    assertEquals("barbar", StringUtils.replaceEach("foofoo", new String[]{"foo"}, new String[]{"bar"}));
}

@Test
public void testReplaceTwoPatterns() {
    assertEquals("barbaz", StringUtils.replaceEach("foobar",
            new String[]{"foo", "bar"},
            new String[]{"bar", "baz"}));
}

@Test
public void testReplaceNone() {
    assertEquals("foofoo", StringUtils.replaceEach("foofoo", new String[]{"x"}, new String[]{"bar"}));
}

@Test
public void testStory() {
    assertEquals("Once upon a foo, there was a bar and a baz, and another bar and a cat.",
            StringUtils.replaceEach("Once upon a baz, there was a foo and a bar, and another foo and a cat.",
                    new String[]{"foo", "bar", "baz"},
                    new String[]{"bar", "baz", "foo"})
    );
}

대체 할 첫 단어를 검색하십시오. 문자열에 있으면 발생 전 문자열 부분과 발생 후 문자열 부분에서 되풀이하십시오.

그렇지 않으면 다음 단어를 계속 바꾸십시오.

순진한 구현은 다음과 같습니다.

public static String replaceAll(String input, String[] search, String[] replace) {
  return replaceAll(input, search, replace, 0);
}

private static String replaceAll(String input, String[] search, String[] replace, int i) {
  if (i == search.length) {
    return input;
  }
  int j = input.indexOf(search[i]);
  if (j == -1) {
    return replaceAll(input, search, replace, i + 1);
  }
  return replaceAll(input.substring(0, j), search, replace, i + 1) +
         replace[i] +
         replaceAll(input.substring(j + search[i].length()), search, replace, i);
}

샘플 사용법 :

String input = "Once upon a baz, there was a foo and a bar.";
String[] search = new String[] { "foo", "bar", "baz" };
String[] replace = new String[] { "bar", "baz", "foo" };
System.out.println(replaceAll(input, search, replace));

산출:

Once upon a foo, there was a bar and a baz.

덜 순진한 버전 :

public static String replaceAll(String input, String[] search, String[] replace) {
  StringBuilder sb = new StringBuilder();
  replaceAll(sb, input, 0, input.length(), search, replace, 0);
  return sb.toString();
}

private static void replaceAll(StringBuilder sb, String input, int start, int end, String[] search, String[] replace, int i) {
  while (i < search.length && start < end) {
    int j = indexOf(input, search[i], start, end);
    if (j == -1) {
      i++;
    } else {
      replaceAll(sb, input, start, j, search, replace, i + 1);
      sb.append(replace[i]);
      start = j + search[i].length();
    }
  }
  sb.append(input, start, end);
}

불행히도 Java String에는 indexOf(String str, int fromIndex, int toIndex)방법 이 없습니다 . 나는 indexOf그것이 정확하지 않다고 여기 에서 구현을 생략 했지만 여기에 게시 된 다양한 솔루션의 대략적인 타이밍과 함께 ideone 에서 찾을 수 있습니다 .


Java 8의 원 라이너 :

    story = Pattern
        .compile(String.format("(?<=%1$s)|(?=%1$s)", "foo|bar"))
        .splitAsStream(story)
        .map(w -> ImmutableMap.of("bar", "foo", "foo", "bar").getOrDefault(w, w))
        .collect(Collectors.joining());
  • Lookaround 정규 표현식 ( ?<=, ?=) : http://www.regular-expressions.info/lookaround.html
  • 단어에 특수 정규식 문자가 포함될 수 있으면 Pattern.quote사용 하여 이스케이프 처리하십시오.
  • 간결함을 위해 구아바 ImmutableMap을 사용하지만 분명히 다른 모든지도 잘 작동합니다.

다음은 일부 사용자에게 흥미로운 Java 8 스트림 가능성입니다.

String word1 = "bar";
String word2 = "foo";

String story = "Once upon a time, there was a foo and a bar.";

// Map is from untranslated word to translated word
Map<String, String> wordMap = new HashMap<>();
wordMap.put(word1, word2);
wordMap.put(word2, word1);

// Split on word boundaries so we retain whitespace.
String translated = Arrays.stream(story.split("\\b"))
    .map(w -> wordMap.getOrDefault(w,  w))
    .collect(Collectors.joining());

System.out.println(translated);

다음은 Java 7의 동일한 알고리즘에 대한 근사치입니다.

String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar.";

// Map is from untranslated word to translated word
Map<String, String> wordMap = new HashMap<>();
wordMap.put(word1, word2);
wordMap.put(word2, word1);

// Split on word boundaries so we retain whitespace.
StringBuilder translated = new StringBuilder();
for (String w : story.split("\\b"))
{
  String tw = wordMap.get(w);
  translated.append(tw != null ? tw : w);
}

System.out.println(translated);

예와 같이 공백으로 구분 된 문장에서 단어를 바꾸려면이 간단한 알고리즘을 사용할 수 있습니다.

  1. 공백 분할 스토리
  2. foo가 bar로 바뀔 경우 각 요소를 바꿉니다.
  3. 배열을 하나의 문자열로 다시 결합

공간 분할이 허용되지 않는 경우이 대체 알고리즘을 따를 수 있습니다. 더 긴 문자열을 먼저 사용해야합니다. 문자열이 foo와 멍청한 경우 먼저 멍청이를 사용해야합니다.

  1. 단어 foo로 나누기
  2. 배열의 각 요소를 foo로 바꿉니다.
  3. 마지막 요소를 제외한 각 요소 뒤에 해당 배열을 다시 추가

다음은 Map을 사용하는 덜 복잡한 답변입니다.

private static String replaceEach(String str,Map<String, String> map) {

         Object[] keys = map.keySet().toArray();
         for(int x = 0 ; x < keys.length ; x ++ ) {
             str = str.replace((String) keys[x],"%"+x);
         }

         for(int x = 0 ; x < keys.length ; x ++) {
             str = str.replace("%"+x,map.get(keys[x]));
         }
         return str;
     }

그리고 방법은

Map<String, String> replaceStr = new HashMap<>();
replaceStr.put("Raffy","awesome");
replaceStr.put("awesome","Raffy");
String replaced = replaceEach("Raffy is awesome, awesome awesome is Raffy Raffy", replaceStr);

결과 : awesome은 Raffy, Raffy Raffy는 굉장합니다


대체 할 검색 문자열을 여러 번 처리 할 수있게하려면 각 검색어에서 문자열을 분할 한 다음 바꾸면 쉽게 수행 할 수 있습니다. 예를 들면 다음과 같습니다.

String regex = word1 + "|" + word2;
String[] values = Pattern.compile(regex).split(story);

String result;
foreach subStr in values
{
   subStr = subStr.replace(word1, word2);
   subStr = subStr.replace(word2, word1);
   result += subStr;
}

다음 코드 블록으로 목표를 달성 할 수 있습니다.

String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, in a foo, there was a foo and a bar.";
story = String.format(story.replace(word1, "%1$s").replace(word2, "%2$s"),
    word2, word1);

순서에 관계없이 단어를 대체합니다. 이 원칙을 다음과 같은 유틸리티 메소드로 확장 할 수 있습니다.

private static String replace(String source, String[] targets, String[] replacements) throws IllegalArgumentException {
    if (source == null) {
        throw new IllegalArgumentException("The parameter \"source\" cannot be null.");
    }

    if (targets == null || replacements == null) {
        throw new IllegalArgumentException("Neither parameters \"targets\" or \"replacements\" can be null.");
    }

    if (targets.length == 0 || targets.length != replacements.length) {
        throw new IllegalArgumentException("The parameters \"targets\" and \"replacements\" must have at least one item and have the same length.");
    }

    String outputMask = source;
    for (int i = 0; i < targets.length; i++) {
        outputMask = outputMask.replace(targets[i], "%" + (i + 1) + "$s");
    }

    return String.format(outputMask, (Object[])replacements);
}

다음과 같이 소비됩니다.

String story = "Once upon a time, in a foo, there was a foo and a bar.";
story = replace(story, new String[] { "bar", "foo" },
    new String[] { "foo", "bar" }));

이것은 작동하며 간단합니다.

public String replaceBoth(String text, String token1, String token2) {            
    return text.replace(token1, "\ufdd0").replace(token2, token1).replace("\ufdd0", token2);
    }

당신은 이것을 다음과 같이 사용합니다 :

replaceBoth("Once upon a time, there was a foo and a bar.", "foo", "bar");

참고 : 이것은 문자를 포함하지 않는 문자열에 의존 \ufdd0합니다. 문자는 유니 코드에서 내부 용 으로 영구적으로 예약 한 문자입니다 ( http://www.unicode.org/faq/private_use.html 참조 ).

나는 그것이 필요하다고 생각하지 않지만, 절대 안전을 원한다면 다음을 사용할 수 있습니다.

public String replaceBoth(String text, String token1, String token2) {
    if (text.contains("\ufdd0") || token1.contains("\ufdd0") || token2.contains("\ufdd0")) throw new IllegalArgumentException("Invalid character.");
    return text.replace(token1, "\ufdd0").replace(token2, token1).replace("\ufdd0", token2);
    }

한 번만 발생

입력에 각 스왑 가능한 문자열이 한 번만 나타나는 경우 다음을 수행 할 수 있습니다.

바꾸기를 진행하기 전에 단어의 출현 지수를 얻으십시오. 그 후 우리는 이러한 색인에서 찾은 단어 만 대체하고 모든 경우를 대체하지는 않습니다. 이 솔루션은 같은 StringBuilder중간체를 사용 하거나 생성하지 않습니다 .StringString.replace()

한 가지 알아 두어야 할 사항 : 교체 가능한 단어의 길이가 다른 경우 첫 번째 교체 후 두 번째 색인이 첫 번째 단어가 두 번째 이전에 나타나는 경우 두 길이의 차이와 정확히 일치 할 수 있습니다. 따라서 두 번째 색인을 정렬하면 길이가 다른 단어를 바꾸는 경우에도 작동합니다.

public static String swap(String src, String s1, String s2) {
    StringBuilder sb = new StringBuilder(src);
    int i1 = src.indexOf(s1);
    int i2 = src.indexOf(s2);

    sb.replace(i1, i1 + s1.length(), s2); // Replace s1 with s2
    // If s1 was before s2, idx2 might have changed after the replace
    if (i1 < i2)
        i2 += s2.length() - s1.length();
    sb.replace(i2, i2 + s2.length(), s1); // Replace s2 with s1

    return sb.toString();
}

임의의 발생 횟수 스와핑

이전의 경우와 유사하게 먼저 단어의 색인 (발생)을 수집하지만이 경우에는 단어가 아닌 각 단어의 정수 목록이 int됩니다. 이를 위해 다음과 같은 유틸리티 방법을 사용합니다.

public static List<Integer> occurrences(String src, String s) {
    List<Integer> list = new ArrayList<>();
    for (int idx = 0;;)
        if ((idx = src.indexOf(s, idx)) >= 0) {
            list.add(idx);
            idx += s.length();
        } else
            return list;
}

그리고 이것을 사용하여 우리는 교체 후 인덱스를 수정하지 않아도되도록 인덱스를 낮추어 단어를 다른 단어로 바꿉니다 (두 개의 교체 가능한 단어 사이를 번갈아 가야 할 수도 있음).

public static String swapAll(String src, String s1, String s2) {
    List<Integer> l1 = occurrences(src, s1), l2 = occurrences(src, s2);

    StringBuilder sb = new StringBuilder(src);

    // Replace occurrences by decreasing index, alternating between s1 and s2
    for (int i1 = l1.size() - 1, i2 = l2.size() - 1; i1 >= 0 || i2 >= 0;) {
        int idx1 = i1 < 0 ? -1 : l1.get(i1);
        int idx2 = i2 < 0 ? -1 : l2.get(i2);
        if (idx1 > idx2) { // Replace s1 with s2
            sb.replace(idx1, idx1 + s1.length(), s2);
            i1--;
        } else { // Replace s2 with s1
            sb.replace(idx2, idx2 + s2.length(), s1);
            i2--;
        }
    }

    return sb.toString();
}

다음을 사용하여이를 수행하는 방법을 쉽게 작성할 수 있습니다 String.regionMatches.

public static String simultaneousReplace(String subject, String... pairs) {
    if (pairs.length % 2 != 0) throw new IllegalArgumentException(
        "Strings to find and replace are not paired.");
    StringBuilder sb = new StringBuilder();
    outer:
    for (int i = 0; i < subject.length(); i++) {
        for (int j = 0; j < pairs.length; j += 2) {
            String find = pairs[j];
            if (subject.regionMatches(i, find, 0, find.length())) {
                sb.append(pairs[j + 1]);
                i += find.length() - 1;
                continue outer;
            }
        }
        sb.append(subject.charAt(i));
    }
    return sb.toString();
}

테스트 :

String s = "There are three cats and two dogs.";
s = simultaneousReplace(s,
    "cats", "dogs",
    "dogs", "budgies");
System.out.println(s);

산출:

3 마리의 개와 2 마리의 budgie가 있습니다.

즉시 명백하지는 않지만 이와 같은 기능은 여전히 ​​교체가 지정된 순서에 따라 달라질 수 있습니다. 치다:

String truth = "Java is to JavaScript";
truth += " as " + simultaneousReplace(truth,
    "JavaScript", "Hamster",
    "Java", "Ham");
System.out.println(truth);

산출:

Ham은 Hamster와 마찬가지로 Java는 JavaScript를

그러나 교체를 역전하십시오.

truth += " as " + simultaneousReplace(truth,
    "Java", "Ham",
    "JavaScript", "Hamster");

산출:

Ham은 HamScript와 마찬가지로 Java는 JavaScript를

죄송합니다! :)

따라서 PHP 함수 와 같이 가장 긴 일치 항목 을 찾는 것이 유용한 경우가 strtr있습니다. 이 버전의 메소드는 다음을 수행합니다.

public static String simultaneousReplace(String subject, String... pairs) {
    if (pairs.length % 2 != 0) throw new IllegalArgumentException(
        "Strings to find and replace are not paired.");
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < subject.length(); i++) {
        int longestMatchIndex = -1;
        int longestMatchLength = -1;
        for (int j = 0; j < pairs.length; j += 2) {
            String find = pairs[j];
            if (subject.regionMatches(i, find, 0, find.length())) {
                if (find.length() > longestMatchLength) {
                    longestMatchIndex = j;
                    longestMatchLength = find.length();
                }
            }
        }
        if (longestMatchIndex >= 0) {
            sb.append(pairs[longestMatchIndex + 1]);
            i += longestMatchLength - 1;
        } else {
            sb.append(subject.charAt(i));
        }
    }
    return sb.toString();
}

위의 방법은 대소 문자를 구분합니다. 대소 문자를 구분하지 않는 버전이 필요한 경우 매개 변수를 String.regionMatches사용할 수 있으므로 위의 내용을 쉽게 수정할 수 있습니다 ignoreCase.


종속성을 원하지 않으면 일회성 변경 만 허용하는 배열을 사용하면됩니다. 이것은 가장 효율적인 솔루션은 아니지만 작동해야합니다.

public String replace(String sentence, String[]... replace){
    String[] words = sentence.split("\\s+");
    int[] lock = new int[words.length];
    StringBuilder out = new StringBuilder();

    for (int i = 0; i < words.length; i++) {
        for(String[] r : replace){
            if(words[i].contains(r[0]) && lock[i] == 0){
                words[i] = words[i].replace(r[0], r[1]);
                lock[i] = 1;
            }
        }

        out.append((i < (words.length - 1) ? words[i] + " " : words[i]));
    }

    return out.toString();
}

그런 다음 일을해야했습니다.

String story = "Once upon a time, there was a foo and a bar.";

String[] a = {"foo", "bar"};
String[] b = {"bar", "foo"};
String[] c = {"there", "Pocahontas"};
story = replace(story, a, b, c);

System.out.println(story); // Once upon a time, Pocahontas was a bar and a foo.

입력에 대해 여러 검색 바꾸기 작업을 수행하고 있습니다. 대체 문자열에 검색 문자열이 포함되어 있으면 원하지 않는 결과가 발생합니다. foo-> bar, bar-foo 예제를 고려하십시오. 각 반복에 대한 결과는 다음과 같습니다.

  1. 옛날 옛적에 foo와 술집이있었습니다. (입력)
  2. 옛날 옛적에 바와 바가있었습니다. (foo-> 바)
  3. 옛날 옛적에 foo와 foo가있었습니다. (bar-> foo, 출력)

돌아 가지 않고 한 번의 반복으로 교체를 수행해야합니다. 무차별 대입 솔루션은 다음과 같습니다.

  1. 일치하는 것을 찾을 때까지 현재 위치에서 입력을 검색하여 여러 검색 문자열을 종료하십시오.
  2. 일치하는 검색 문자열을 해당하는 대체 문자열로 바꿉니다.
  3. 교체 된 문자열 다음에 현재 위치를 다음 문자로 설정
  4. 반복

같은 함수는 String.indexOfAny(String[]) -> int[]{index, whichString}유용 할 것이다. 다음은 가장 효율적인 예는 아닙니다.

private static String replaceEach(String str, String[] searchWords, String[] replaceWords) {
    String ret = "";
    while (str.length() > 0) {
        int i;
        for (i = 0; i < searchWords.length; i++) {
            String search = searchWords[i];
            String replace = replaceWords[i];
            if (str.startsWith(search)) {
                ret += replace;
                str = str.substring(search.length());
                break;
            }
        }
        if (i == searchWords.length) {
            ret += str.substring(0, 1);
            str = str.substring(1);
        }
    }
    return ret;
}

일부 테스트 :

System.out.println(replaceEach(
    "Once upon a time, there was a foo and a bar.",
    new String[]{"foo", "bar"},
    new String[]{"bar", "foo"}
));
// Once upon a time, there was a bar and a foo.

System.out.println(replaceEach(
    "a p",
    new String[]{"a", "p"},
    new String[]{"apple", "pear"}
));
// apple pear

System.out.println(replaceEach(
    "ABCDE",
    new String[]{"A", "B", "C", "D", "E"},
    new String[]{"B", "C", "E", "E", "F"}
));
// BCEEF

System.out.println(replaceEach(
    "ABCDEF",
    new String[]{"ABCDEF", "ABC", "DEF"},
    new String[]{"XXXXXX", "YYY", "ZZZ"}
));
// XXXXXX
// note the order of search strings, longer strings should be placed first 
// in order to make the replacement greedy

IDEONE 데모 IDEONE
데모, 대체 코드


당신은 항상 당신이 문자열의 다른 곳에 나타나지 않을 것이라고 확신하는 단어로 바꿀 수 있습니다.

String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar."
story = story.replace("foo", "StringYouAreSureWillNeverOccur").replace("bar", "word2").replace("StringYouAreSureWillNeverOccur", "word1");

"StringYouAreSureWillNeverOccur"발생 하면 제대로 작동 하지 않습니다.


StringBuilder 사용을 고려하십시오

그런 다음 각 문자열을 시작해야하는 색인을 저장하십시오. 각 위치에 자리 표시 자 문자를 사용하는 경우이를 제거하고 사용자 문자열을 삽입하십시오. 그런 다음 문자열 길이를 시작 위치에 추가하여 끝 위치를 매핑 할 수 있습니다.

String firstString = "???";
String secondString  = "???"

StringBuilder story = new StringBuilder("One upon a time, there was a " 
    + firstString
    + " and a "
    + secondString);

int  firstWord = 30;
int  secondWord = firstWord + firstString.length() + 7;

story.replace(firstWord, firstWord + firstString.length(), userStringOne);
story.replace(secondWord, secondWord + secondString.length(), userStringTwo);

firstString = userStringOne;
secondString = userStringTwo;

return story;

내가 공유 할 수있는 것은 내 자신의 방법입니다.

임시 String temp = "<?>";또는String.Format();

이것은 - "아이디어 만, 정확한 답변 아님"을 통해 콘솔 응용 프로그램에서 작성된 예제 코드입니다 .

static void Main(string[] args)
    {
        String[] word1 = {"foo", "Once"};
        String[] word2 = {"bar", "time"};
        String story = "Once upon a time, there was a foo and a bar.";

        story = Switcher(story,word1,word2);
        Console.WriteLine(story);
        Console.Read();
    }
    // Using a temporary string.
    static string Switcher(string text, string[] target, string[] value)
    {
        string temp = "<?>";
        if (target.Length == value.Length)
        {
            for (int i = 0; i < target.Length; i++)
            {
                text = text.Replace(target[i], temp);
                text = text.Replace(value[i], target[i]);
                text = text.Replace(temp, value[i]);
            }
        }
        return text;
    }

또는 당신은 또한 사용할 수 있습니다 String.Format();

static string Switcher(string text, string[] target, string[] value)
        {
            if (target.Length == value.Length)
            {
                for (int i = 0; i < target.Length; i++)
                {
                    text = text.Replace(target[i], "{0}").Replace(value[i], "{1}");
                    text = String.Format(text, value[i], target[i]);
                }
            }
            return text;
        }

산출: time upon a Once, there was a bar and a foo.


다음은 단어 기반의 내 버전입니다.

class TextReplace
{

    public static void replaceAll (String text, String [] lookup,
                                   String [] replacement, String delimiter)
    {

        String [] words = text.split(delimiter);

        for (int i = 0; i < words.length; i++)
        {

            int j = find(lookup, words[i]);

            if (j >= 0) words[i] = replacement[j];

        }

        text = StringUtils.join(words, delimiter);

    }

    public static  int find (String [] array, String key)
    {

        for (int i = 0; i < array.length; i++)
            if (array[i].equals(key))
                return i;

        return (-1);

    }

}

String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar."

조금 까다로운 방법이지만 더 확인해야합니다.

1. 문자열을 문자형 배열로 변환

   String temp[] = story.split(" ");//assume there is only spaces.

온도에 2.loop 및 교체 foo와 함께 barbar함께 foo다시 교체 문자열을 점점 더 기회가 없기 때문에.


짧은 대답은 ...

String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar.";
story = story.replace("foo", "@"+ word1).replace("bar", word2).replace("@" + word2, word1);
System.out.println(story);

여기에 있는 대답을 사용하여 바꾸려는 모든 문자열을 찾을 수 있습니다.

예를 들어 위의 SO 답변에서 코드를 실행하십시오. 두 개의 색인 테이블을 작성하십시오 (bar와 foo는 문자열에 한 번만 표시되지 않음).이 테이블을 사용하여 문자열에서 대체 할 수 있습니다.

이제 특정 색인 위치를 대체하기 위해 다음을 사용할 수 있습니다.

public static String replaceStringAt(String s, int pos, String c) {
   return s.substring(0,pos) + c + s.substring(pos+1);
}

반면 pos문자열은 위에서 인용 한 색인 테이블에서 시작하는 색인입니다. 각각에 대해 두 개의 인덱스 테이블을 생성했다고 가정하겠습니다. 의 그들을 부르 자 indexBarindexFoo.

이제 교체 할 때마다 교체 할 루프마다 하나씩 두 개의 루프를 실행할 수 있습니다.

for(int i=0;i<indexBar.Count();i++)
replaceStringAt(originalString,indexBar[i],newString);

마찬가지로 다른 루프입니다 indexFoo.

이것은 다른 답변만큼 효율적이지 않을 수 있지만지도 또는 다른 것들보다 이해하기가 더 간단합니다.

이렇게하면 항상 원하는 결과를 얻을 수 있고 각 문자열이 여러 번 나타날 수 있습니다. 각 발생의 색인을 저장하는 한.

또한이 대답은 재귀 나 외부 종속성이 필요하지 않습니다. 복잡성에 관한 한 그것은 적절하게 O (n squared)이며, n은 두 단어의 총합입니다.


이 코드를 개발하여 문제를 해결할 수 있습니다.

public static String change(String s,String s1, String s2) {
   int length = s.length();
   int x1 = s1.length();
   int x2 = s2.length();
   int x12 = s.indexOf(s1);
   int x22 = s.indexOf(s2);
   String s3=s.substring(0, x12);
   String s4 =s.substring(x12+3, x22);
   s=s3+s2+s4+s1;
   return s;
}

주요 용도 change(story,word2,word1).


String word1 = "bar";
String word2 = "foo";

String story = "Once upon a time, there was a foo and a bar."

story = story.replace("foo", "<foo />");
story = story.replace("bar", "<bar />");

story = story.replace("<foo />", word1);
story = story.replace("<bar />", word2);

참고 : https://stackoverflow.com/questions/26791441/how-can-i-replace-two-strings-in-a-way-that-one-does-not-end-up-replacing-the-ot

반응형