Programing

어떻게 하면 한 끈이 다른 끈을 교체하는 것으로 끝나지 않는 방식으로 두 줄을 교체할 수 있을까?

c10106 2022. 4. 28. 20:01
반응형

어떻게 하면 한 끈이 다른 끈을 교체하는 것으로 끝나지 않는 방식으로 두 줄을 교체할 수 있을까?

내가 다음과 같은 코드를 가지고 있다고 하자.

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 StringUtils의 메서드 사용:

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

As a response to criticism: if you use a large enough uncommon string like zq515sqdqs5d5sq1dqs4d1q5dqqé"&é5d4sqjshsjddjhodfqsqc, nvùq^µù;d&€sdq: d: ;)àçàçlala and use that, it is unlikely to the point where I won't even debate it that a user will ever enter this.사용자가 그럴지 여부를 알 수 있는 유일한 방법은 소스 코드를 아는 것 뿐이고 그 시점에서 당신은 전혀 다른 수준의 걱정을 하고 있는 것이다.

그래, 어쩌면 화려한 레그스가 있을지도 몰라.나는 내게서 일어나지 않을 것이라고 알고 있는 읽기 쉬운 것을 선호한다.

또한 논평에서 @David Conrad가 제공한 훌륭한 조언을 반복한다.

가능성이 낮다고 선택한 끈을 교묘하게 사용하지 마십시오.유니코드 개인 사용 영역 U+E000의 문자를 사용하십시오.U+F8FF. 이러한 문자는 합법적으로 입력(일부 응용 프로그램 내에서 응용 프로그램 특정 의미만 가질 수 있으므로)에 있으면 안 되므로 먼저 제거하고 교체할 때 자리 표시자로 사용하십시오.

그리고 :를 사용하여 이와 같은 것을 시도해 볼 수 있다.

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());
옛날 옛적에 술집에 푸가 있었다.

이것은 쉬운 문제가 아니다.검색 대체 매개 변수가 많을수록 더 까다로워지는 겁니다.여러 가지 옵션이 있으며, 추악한-예절적이고, 효율적이고-쓰레기가 많은 팔레트에 흩어져 있다.

  • 사용하다StringUtils.replaceEach@AlanHay가 권고한 대로 아파치 커먼즈에서 왔다.프로젝트에 새로운 종속성을 자유롭게 추가할 수 있는 경우 이것은 좋은 옵션이다.운이 좋을 수도 있다. 종속성이 프로젝트에 이미 포함되어 있을 수 있음

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

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

    이것은 몇 가지 이유로 훌륭한 접근법이 아니다. 첫 번째 단계에서 사용된 태그가 정말로 고유한지 확인해야 한다. 이것은 실제로 필요한 것보다 더 많은 문자열 교체 작업을 수행한다.

  • 모든 패턴에서 regex를 작성하고 @arshajii가 제안한 방법과 함께 사용한다.이것은 끔찍하지는 않지만, 그리 대단하지도 않다. 왜냐하면, 레그스를 짓는 것은 일종의 해커적인 일이고, 그것은 그것과 관련이 있기 때문이다.StringBuffer 에에에에에에에에에 에에에에에에 에에에에 에에에에에 인.StringBuilder.

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

  • 본문을 단어로 나누고 자바 8 스트림을 사용하여 @msandiford가 제안한 대로 우아하게 교체를 수행하지만, 물론 그것은 단어 경계에서 분할하는 것이 괜찮아야만 작동하기 때문에 일반적인 해결책으로 적합하지 않다.

아파치 구현에서 차용한 아이디어를 바탕으로 한 내 버전을 소개한다.간단하지도 않고 우아하지도 않지만, 효과가 있고, 불필요한 조치 없이 비교적 효율적이어야 한다.간단히 말해서, 텍스트에서 일치하는 다음 검색 패턴을 반복적으로 찾아내고, 다음 검색 패턴을 사용하는 것이다.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);
}

아쉽게도 자바네 집.String없다indexOf(String str, int fromIndex, int toIndex)방법의 실시를 생략했다.indexOf정확하지는 않지만, IDE에서 찾을 수 있고 여기에 게시된 다양한 해결책의 대략적인 타이밍도 확인할 수 있다.

Java 8의 One-liner:

    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());
  • 정규식 둘러보기(?<=?=http://www.regular-expressions.info/lookaround.html
  • 단어에 특수 정규식 문자가 포함될 수 있는 경우 패턴을 사용하십시오.그들을 피하려고 인용하다
  • 나는 구체성을 위해 구아바 불변지도를 사용하지만, 분명히 다른 지도도 그 일을 할 것이다.

다음은 일부 사용자에게 흥미로운 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);

다음은 자바 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가 바 및 바바사로 교체하는 경우 각 요소 교체
  3. 어레이를 다시 하나의 문자열로 결합

공간 분할이 허용되지 않으면 이 대체 알고리즘을 따를 수 있다.먼저 긴 줄을 사용해야 해.현악기가 foo이고 바보라면 먼저 바보짓을 하고 나서 foo를 써야 한다.

  1. foo라는 단어로 나누다.
  2. 막대를 배열의 각 요소로 교체
  3. 마지막 요소를 제외한 각 요소 뒤에 막대를 추가하여 해당 어레이에 다시 결합

여기 맵을 사용하는 덜 복잡한 대답이 있다.

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

출력: 대박 라피, 대박 라피

교체할 검색 문자열의 여러 발생을 처리할 수 있으려면 각 검색어에 있는 문자열을 분할한 다음 해당 문자열을 교체하면 된다.예를 들면 다음과 같다.

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중간을 생성하지 않는다.String와 같다String.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();
}

다음을 사용하여 이 작업을 수행하는 방법을 쓰는 것은 쉽다.

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

출력:

개 세 마리와 새끼 두 마리가 있다.

즉각적으로 명백하지는 않지만, 이와 같은 기능은 여전히 교체가 지정된 순서에 따라 달라질 수 있다.고려 사항:

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

출력:

자바스크립트는 햄스터와 마찬가지로 자바스크립트는 햄스터와 같다.

그러나 교체품은 다음과 같이 반대로 한다.

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

출력:

자바스크립트는 함스크립트와 마찬가지로 자바스크립트는 함스크립트와 같다.

아이고! :)

따라서 (예를 들어 PHP의 기능이 그러하듯이) 가장 일치점을 반드시 찾는 것이 유용할 때도 있다.이 방법의 버전은 다음과 같이 할 수 있다.

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. 옛날 옛적에 푸와 술집이 있었다.(iii)
  2. 옛날에 술집과 술집이 있었다. (foo->)
  3. 옛날에 foo와 foo가 있었다.(bar->foo, 출력)

당신은 돌아가지도 않고 한 번의 반복으로 교체 작업을 수행해야 한다.흉포한 용액은 다음과 같다.

  1. 일치 항목이 발견될 때까지 현재 위치에서 끝까지 여러 검색 문자열 검색
  2. 일치하는 검색 문자열을 해당 대체 문자열로 바꾸기
  3. 교체된 문자열 뒤에 현재 위치를 다음 문자로 설정
  4. 반복하다

다음과 같은 기능String.indexOfAny(String[]) -> int[]{index, whichString}쓸가ga 있을 거 아님가장 효율적인 예는 다음과 같다.

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

은 c -"Idea Only, Not Accurrent Answer"를 통해 콘솔 애플리케이션에서 만든 나의 예 코드다.

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.온도조절 및 교체foo와 함께bar그리고bar와 함께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 답변에서 코드를 실행하십시오.두 개의 인덱스 테이블을 만들고(바와 foo가 문자열에서 한 번만 나타나지 않도록 함) 해당 테이블을 사용하여 문자열에서 해당 인덱스를 교체하십시오.

이제 특정 인덱스 위치를 바꾸려면 다음을 사용하십시오.

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

반면에pos(위에서 인용한 인덱스 테이블에서) 문자열이 시작되는 인덱스 입니다.각 인덱스에 대해 두 개의 테이블을 생성했다고 가정해 봅시다.그들을 부르자.indexBar그리고indexFoo.

이제 교체할 때는 각 교체마다 하나씩, 간단하게 두 개의 루프를 실행할 수 있다.

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

마찬가지로 다음에 대한 또 다른 루프indexFoo.

이것은 여기의 다른 답들만큼 효율적이지 않을 수도 있지만 지도나 다른 것들보다 이해하기 더 간단하다.

이렇게 하면 항상 원하는 결과를 얻을 수 있으며 각 문자열이 여러 번 발생할 수 있다.각 발생의 인덱스를 저장하는 한.

또한 이 답변은 재귀나 외부 의존을 필요로 하지 않는다.복잡성에 관한 한 그것은 O(n 제곱)인 반면, 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);

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

반응형