C #에서 여러 문자열 요소 바꾸기


88

이 작업을 수행하는 더 좋은 방법이 있습니까?

MyString.Trim().Replace("&", "and").Replace(",", "").Replace("  ", " ")
         .Replace(" ", "-").Replace("'", "").Replace("/", "").ToLower();

하나의 작업으로 유지하기 위해 문자열 클래스를 확장했지만 더 빠른 방법이 있습니까?

public static class StringExtension
{
    public static string clean(this string s)
    {
        return s.Replace("&", "and").Replace(",", "").Replace("  ", " ")
                .Replace(" ", "-").Replace("'", "").Replace(".", "")
                .Replace("eacute;", "é").ToLower();
    }
}

재미를 위해 (그리고 의견에서 논쟁을 멈추기 위해) 아래의 다양한 예를 벤치마킹하는 요점을 밀었습니다.

https://gist.github.com/ChrisMcKee/5937656

정규식 옵션은 매우 점수가 높습니다. 사전 옵션이 가장 빨리 나타납니다. stringbuilder 교체의 긴 감기 버전은 짧은 손보다 약간 빠릅니다.


1
벤치 마크에있는 내용에 따르면 사전 버전이 StringBuilder 솔루션보다 빠르게 만드는 것으로 의심되는 모든 대체 작업을 수행하지 않는 것 같습니다.
두꺼비

1
2009 년부터 @toad Hi; 나는 그 눈부신 실수에 대해 4 월에 아래에 코멘트를 추가했습니다. 요점은 D를 건너 뛰었지만 업데이트되었습니다. 사전 버전은 여전히 ​​더 빠릅니다.
Chris McKee 2014 년


1
@TotZam은 적어도 물건을 신고하기 전에 날짜를 확인하십시오. 이는 2012 년부터 2009 먹으 렴에서입니다
크리스 맥키

여기에 많은 답변이 성능과 관련이있는 것 같기 때문에 Andrej Adamanko의 답변 이 많은 교체에서 가장 빠르다는 점을 지적해야한다고 생각합니다 . 그의 대답에 명시된 것처럼 특히 큰 입력 문자열에서 .Replace () 연결보다 확실히 빠릅니다.
person27

답변:


125

더 빨리-아니요. 더 효과적-네, StringBuilder수업 을 사용한다면 . 구현시 각 작업은 상황에서 성능을 저하시킬 수있는 문자열 사본을 생성합니다. 문자열은 변경 불가능한 객체이므로 각 작업은 수정 된 복사본 만 반환합니다.

이 메서드가 Strings상당한 길이의 배수 에서 활발하게 호출 될 것으로 예상하는 경우 해당 구현을 StringBuilder클래스 로 "마이그레이션"하는 것이 좋습니다 . 이를 통해 모든 수정은 해당 인스턴스에서 직접 수행되므로 불필요한 복사 작업을 절약 할 수 있습니다.

public static class StringExtention
{
    public static string clean(this string s)
    {
        StringBuilder sb = new StringBuilder (s);

        sb.Replace("&", "and");
        sb.Replace(",", "");
        sb.Replace("  ", " ");
        sb.Replace(" ", "-");
        sb.Replace("'", "");
        sb.Replace(".", "");
        sb.Replace("eacute;", "é");

        return sb.ToString().ToLower();
    }
}

2
명확성을 위해 사전 답변은 가장 빠른 stackoverflow.com/a/1321366/52912
Chris McKee 2013

3
gist.github.com/ChrisMcKee/5937656 의 벤치 마크에서 사전 테스트가 완료되지 않았습니다. 모든 대체를 수행하지 않고 ""가 ""가 아니라 ""를 대체합니다. 모든 교체를 수행하지 않는 것이 이유가 될 수 있으며 벤치 마크에서 가장 빠른 이유입니다. 정규식 교체도 완료되지 않았습니다. 그러나 가장 중요한 것은 TestData 문자열이 매우 짧다는 것입니다. 수락 된 답변 상태와 마찬가지로 StringBuilder가 이점을 얻으려면 문자열의 길이가 길어야합니다. 10kB, 100kB 및 1MB의 문자열로 벤치 마크를 반복 해 주시겠습니까?
Leif

좋은 점입니다. 현재로서는 URL 정리에 사용되었으므로 100kb-1mb에서의 테스트는 비현실적이었습니다. 벤치 마크를 업데이트하여 전체를 사용하는 것은 실수였습니다.
Chris McKee 2014

최상의 성능을 위해 문자를 반복하고 직접 교체하십시오. 그러나 문자열이 두 개 이상인 경우에는 지루할 수 있습니다 (찾기는 한 번에 여러 문자를 비교하도록 강제하는 반면, 교체하려면 더 많은 메모리를 할당하고 나머지 문자열을 이동해야 함).
Chayim Friedman

14

단순히 예쁜 솔루션을 찾고 있고 몇 나노초를 절약 할 필요가 없다면 LINQ 설탕은 어떻습니까?

var input = "test1test2test3";
var replacements = new Dictionary<string, string> { { "1", "*" }, { "2", "_" }, { "3", "&" } };

var output = replacements.Aggregate(input, (current, replacement) => current.Replace(replacement.Key, replacement.Value));

Gist의 예제 C와 유사합니다 (위를 보면 더
Chris McKee

1
당신이 기능적 표현을 절차 적 표현보다 "Uglier"로 정의하는 것이 흥미 롭습니다.
TimS 2014 년

그것에 대해 논쟁하지 않을 것입니다. 그것의 단지 선호. 당신이 말했듯이, linq는 단순히 문법적인 설탕입니다. 나는 이미 :) 코드 위의 해당 넣어이라고 말했다로
크리스 맥키

14

더 효율적입니다.

public static class StringExtension
{
    public static string clean(this string s)
    {
        return new StringBuilder(s)
              .Replace("&", "and")
              .Replace(",", "")
              .Replace("  ", " ")
              .Replace(" ", "-")
              .Replace("'", "")
              .Replace(".", "")
              .Replace("eacute;", "é")
              .ToString()
              .ToLower();
    }
}

읽기가 정말 어렵습니다. 나는 그것이 무엇을하는지 당신이 알고 있다고 확신하지만 주니어 개발자는 실제로 일어나는 일에 그의 머리를 긁을 것입니다. 나는 동의한다. 나는 항상 무언가를 쓰는 반바지 손을 찾는다. 그러나 그것은 단지 나의 만족을위한 것이었다. 다른 사람들은 엉망진창에 놀랐습니다.
Piotr Kula 2013

3
이것은 실제로 더 느립니다. BenchmarkOverhead는 ... ... StringClean-user151323 ... 2843ms StringClean-TheVillageIdiot을을 13ms 재방송에 따라 다름 2921ms하지만 대답은 승리 gist.github.com/anonymous/5937596을
크리스 맥키

11

좀 더 읽기 쉬울까요?

    public static class StringExtension {

        private static Dictionary<string, string> _replacements = new Dictionary<string, string>();

        static StringExtension() {
            _replacements["&"] = "and";
            _replacements[","] = "";
            _replacements["  "] = " ";
            // etc...
        }

        public static string clean(this string s) {
            foreach (string to_replace in _replacements.Keys) {
                s = s.Replace(to_replace, _replacements[to_replace]);
            }
            return s;
        }
    }

또한 StringBuilder에 대한 New In Town의 제안을 추가하십시오.


5
그것은이 같은 더 읽을 것 :private static Dictionary<string, string> _replacements = new Dictionary<string, string>() { {"&", "and"}, {",", ""}, {" ", " "} /* etc */ };
ANeves 생각 SE 악한

2
또는 물론 ... private static readonly Dictionary <string, string> Replacements = new Dictionary <string, string> () {{ "&", "and"}, { ",", ""}, { "", ""} / * 등 * /}; public static string Clean (this string s) {return Replacements.Keys.Aggregate (s, (current, toReplace) => current.Replace (toReplace, Replacements [toReplace])); }
크리스 맥키

2
-1 : 사전을 사용하는 것은 여기에 아무런 의미가 없습니다. 그냥를 사용 List<Tuple<string,string>>. 이것은 또한 교체 순서를 변경하고 예를 들어만큼 빠르지 않습니다 s.Replace("a").Replace("b").Replace("c"). 이것을 사용하지 마십시오!
Thomas

6

제안 된 솔루션에서 최적화 할 수있는 한 가지가 있습니다. 를 많이 호출 Replace()하면 코드가 동일한 문자열을 여러 번 통과하도록합니다. 매우 긴 문자열을 사용하면 CPU 캐시 용량 누락으로 인해 솔루션이 느려질 수 있습니다. 한 번에 여러 문자열을 교체하는 것을 고려해야 수도 있습니다 .


1
많은 답변이 성능에 대해 우려하는 것 같습니다.이 경우 이것이 가장 좋습니다. 이 예제에서는 일치하는 사전을 사용하여 일치를 기반으로 예상 값을 반환하는 String.Replace 의 문서화 된 오버로드 이기 때문에 간단 합니다. 이해하기 간단해야합니다.
person27

4

linq를 사용하는 또 다른 옵션은

[TestMethod]
public void Test()
{
  var input = "it's worth a lot of money, if you can find a buyer.";
  var expected = "its worth a lot of money if you can find a buyer";
  var removeList = new string[] { ".", ",", "'" };
  var result = input;

  removeList.ToList().ForEach(o => result = result.Replace(o, string.Empty));

  Assert.AreEqual(expected, result);
}

선언 var removeList = new List<string> { /*...*/ };한 다음 removeList.ForEach( /*...*/ );코드를 호출 하고 단순화 할 수 있습니다. 또한 발견 된 모든 문자열이로 대체 되기 때문에 질문에 완전히 대답하지 않습니다 String.Empty.
Tok

2

비슷한 일을하고 있지만 제 경우에는 직렬화 / 역 직렬화를 수행하고 있으므로 양방향으로 이동할 수 있어야합니다. 나는 string [] []을 사용하는 것이 초기화를 포함하여 딕셔너리와 거의 동일하게 작동한다는 것을 알지만, 다른 방향으로도 갈 수 있고, 딕셔너리가 실제로 설정하지 않은 원래 값으로 대체물을 반환 할 수 있습니다.

편집 : Dictionary<Key,List<Values>>문자열 [] []과 동일한 결과를 얻기 위해 사용할 수 있습니다 .


-1
string input = "it's worth a lot of money, if you can find a buyer.";
for (dynamic i = 0, repl = new string[,] { { "'", "''" }, { "money", "$" }, { "find", "locate" } }; i < repl.Length / 2; i++) {
    input = input.Replace(repl[i, 0], repl[i, 1]);
}

2
답변에 맥락을 추가하는 것을 고려해야합니다. 그것이 무엇을하는지에 대한 간략한 설명과 관련이 있다면 왜 당신이했던 방식으로 작성했는지.
Neil
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.