문자열에서 특수 문자를 제거하는 가장 효율적인 방법


266

문자열에서 모든 특수 문자를 제거하고 싶습니다. 허용되는 문자는 AZ (대문자 또는 소문자), 숫자 (0-9), 밑줄 (_) 또는 점 기호 (.)입니다.

나는 다음과 같이 작동하지만 효과적이지 않다고 생각한다.

    public static string RemoveSpecialCharacters(string str)
    {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str.Length; i++)
        {
            if ((str[i] >= '0' && str[i] <= '9')
                || (str[i] >= 'A' && str[i] <= 'z'
                    || (str[i] == '.' || str[i] == '_')))
                {
                    sb.Append(str[i]);
                }
        }

        return sb.ToString();
    }

가장 효율적인 방법은 무엇입니까? 정규식은 어떤 모양이며 일반 문자열 조작과 어떻게 비교됩니까?

청소할 줄은 보통 10 ~ 30 자 정도의 짧은 길이입니다.


5
더 효율적이지 않기 때문에 이것을 대답에 넣지 않을 것이지만 char.IsLetterOrDigit ()와 같은 정적 문자 메소드는 if 문에서 적어도 더 읽기 쉽게 만들 수 있습니다.
마틴 해리스

5
나는 알파벳순이 아닌 6 문자를 가져 와서 하나만 원하는 (밑줄) 6 문자를 가져 오는 것이 안전하다는 것을 확신하지 못합니다.
Steven Sudit

4
코드를 더 읽기 쉽게 만드는 데 집중하십시오. 초당 500 회와 같은 루프에서이 작업을 수행하지 않는 한 효율성은 크게 중요하지 않습니다. 정규 표현식을 사용하면 훨씬 쉽게 읽을 수 있습니다 .l
Byron Whitlock

4
바이런, 당신은 아마도 가독성을 강조 할 필요가있을 것입니다. 그러나 정규 표현식을 읽을 수 있다는 것에 회의적입니다. :-)
Steven Sudit

2
정규식은 읽을 수 있거나 읽을 수없는 독일어와 비슷합니다. 그것은 당신이 그것을 알고 있는지 아닌지에 달려 있습니다 (두 경우 모두 당신은 지금은 모든 다음 이해가되지 않는 문법 규칙을
만납니다

답변:


325

왜 당신의 방법이 비효율적이라고 생각합니까? 실제로 가장 효율적인 방법 중 하나입니다.

물론 문자를 로컬 변수로 읽거나 열거자를 사용하여 배열 액세스 수를 줄여야합니다.

public static string RemoveSpecialCharacters(this string str) {
   StringBuilder sb = new StringBuilder();
   foreach (char c in str) {
      if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '.' || c == '_') {
         sb.Append(c);
      }
   }
   return sb.ToString();
}

이와 같은 방법을 효율적으로 만드는 것은 확장 성이 뛰어나다는 것입니다. 실행 시간은 문자열 길이를 기준으로합니다. 큰 줄에 사용하면 놀라운 놀라움이 없습니다.

편집 :
빠른 성능 테스트를 수행하여 24 개의 문자열로 각 기능을 백만 번 실행했습니다. 결과는 다음과 같습니다.

원래 기능 : 54.5ms
내 제안 된 변경 사항 : 47.1 ms.
StringBuilder 용량을 설정 한 광산 : 43.3ms
정규식 : 294.4ms

편집 2 : 위 코드에서 AZ와 az의 차이점을 추가했습니다. (성능 테스트를 다시 실행했으며 눈에 띄는 차이는 없습니다.)

편집 3 :
lookup + char [] 솔루션을 테스트했으며 약 13ms에서 실행됩니다.

물론 지불해야 할 가격은 거대한 조회 테이블을 초기화하고 메모리에 유지하는 것입니다. 글쎄, 그것은 그렇게 많은 데이터는 아니지만 그런 사소한 기능을위한 것입니다 ...

private static bool[] _lookup;

static Program() {
   _lookup = new bool[65536];
   for (char c = '0'; c <= '9'; c++) _lookup[c] = true;
   for (char c = 'A'; c <= 'Z'; c++) _lookup[c] = true;
   for (char c = 'a'; c <= 'z'; c++) _lookup[c] = true;
   _lookup['.'] = true;
   _lookup['_'] = true;
}

public static string RemoveSpecialCharacters(string str) {
   char[] buffer = new char[str.Length];
   int index = 0;
   foreach (char c in str) {
      if (_lookup[c]) {
         buffer[index] = c;
         index++;
      }
   }
   return new string(buffer, 0, index);
}

4
나는 동의한다. 내가 할 유일한 다른 변화는 초기 용량 인수를 StringBuilder 생성자 "= new StringBuilder (str.Length)"에 추가하는 것입니다.
David

2
내 테스트에 따르면 char[]버퍼 대신 버퍼를 사용하여 내 대답에 StringBuilder약간의 우위가 있습니다. (하지만 읽기가 쉽지 않기 때문에 작은 성능 이점은 그만한 가치가 없을 것입니다.)
LukeH

1
@Steven : 그럴 수도 있지만 벤치 마크는 그 자체입니다! 필자의 테스트에서 char[]버퍼를 사용하면 StringBuilder수만 자 길이의 문자열로 확장 할 때도 (보다) 성능이 약간 향상 됩니다.
LukeH

10
@ downvoter : 왜 downvote입니까? 당신이 잘못 생각하는 것을 설명하지 않으면 대답을 향상시킬 수 없습니다.
Guffa

2
@SILENT : 아니요. 그렇지 않습니다. 단 한 번만해야합니다. 메소드를 호출 할 때마다 (그리고 메소드를 자주 호출 할 때마다) 큰 배열을 할당하면 메소드가 훨씬 느려져 가비지 콜렉터에 많은 작업이 발생합니다.
Guffa

195

글쎄, 당신이 정말로 당신의 기능에서 성능을 짜낼 필요가 없다면, 유지하고 이해하기 가장 쉬운 것을 따라 가십시오. 정규식은 다음과 같습니다.

추가 성능을 위해 사전 컴파일하거나 첫 번째 호출에서 컴파일하도록 지시 할 수 있습니다 (이후의 호출은 더 빠릅니다).

public static string RemoveSpecialCharacters(string str)
{
    return Regex.Replace(str, "[^a-zA-Z0-9_.]+", "", RegexOptions.Compiled);
}

1
나는 이것이 아마도 사전 컴파일 된 경우 OP의 접근법보다 빠를 정도로 복잡한 쿼리 일 것입니다. 그러나이를 뒷받침 할 증거는 없습니다. 테스트해야합니다. 속도가 크게 느려지지 않는 한, 읽기 및 유지 관리가 더 쉽기 때문에이 방법을 선택합니다. +1
rmeador

6
매우 간단한 정규 표현식 (역 추적이나 복잡한 내용이 없음)이 너무 빠릅니다.

9
@rmeador : 컴파일하지 않고 약 5 배 느리고 컴파일 된 방법보다 3 배 느립니다. 그래도 여전히 10 배 더 단순 :-D
user7116

6
정규 표현식은 마법의 망치가 아니며 손으로 최적화 된 코드보다 빠르지 않습니다.
크리스찬 클라우 저

2
최적화에 대한 Knuth의 유명한 인용문을 기억하는 사람들에게는 여기서부터 시작할 수 있습니다. 그런 다음 천분의 일의 밀리 초 성능이 필요하면 다른 기술 중 하나를 사용하십시오.
John

15

간단한 조회 테이블을 만드는 것이 좋습니다.이 테이블은 정적 생성자에서 초기화하여 문자 조합을 유효하게 설정할 수 있습니다. 이를 통해 빠른 단일 검사를 수행 할 수 있습니다.

편집하다

또한 속도를 높이기 위해 StringBuilder의 용량을 입력 문자열의 길이로 초기화하려고합니다. 재 할당을 피할 수 있습니다. 이 두 가지 방법을 함께 사용하면 속도와 유연성이 모두 향상됩니다.

다른 편집

컴파일러가 최적화 할 수 있다고 생각하지만 효율성뿐만 아니라 스타일 측면에서도 foreach를 권장합니다.


배열의 경우 for와는 foreach비슷한 코드를 생성합니다. 그래도 문자열에 대해서는 모른다. JIT가 String과 같은 배열과 같은 특성을 알고 있다고 의심합니다.
크리스찬 클라우 저

1
JIT는 [joke removed]보다 배열과 같은 문자열 특성에 대해 더 많이 알고 있습니다. Anders etal은 .net에서 문자열에 관한 모든 것을 최적화하기 위해 많은 작업을 수행했습니다

HashSet <char>을 사용 하여이 작업을 수행했으며 그의 방법보다 약 2 배 느립니다. bool []을 사용하면 OP에있는 버전보다 거의 빠르지 않습니다 (0.0469ms / iter v. 0.0559ms / iter) ... 읽기 어려운 문제가 있습니다.
user7116

1
bool 배열과 int 배열 사용 간의 성능 차이를 볼 수 없었습니다. 부울 배열을 사용하면 조회 테이블이 256kb에서 64kb로 줄어 들지만 여전히 사소한 기능에 대한 많은 데이터가 있습니다 ... 약 30 % 빠릅니다.
Guffa

1
@Guffa 2) 영숫자와 소수의 기본 라틴 문자 만 유지한다는 점을 감안하면 낮은 바이트에 대한 테이블 만 필요하므로 크기는 실제로 문제가되지 않습니다. 우리가 범용 적이기를 원한다면 표준 유니 코드 기술은 이중 간접적입니다. 다시 말해, 256 개의 테이블 참조 테이블은 대부분 동일한 빈 테이블을 가리 킵니다.
Steven Sudit

12
public static string RemoveSpecialCharacters(string str)
{
    char[] buffer = new char[str.Length];
    int idx = 0;

    foreach (char c in str)
    {
        if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z')
            || (c >= 'a' && c <= 'z') || (c == '.') || (c == '_'))
        {
            buffer[idx] = c;
            idx++;
        }
    }

    return new string(buffer, 0, idx);
}

1
+1, 테스트되었으며 StringBuilder보다 약 40 % 빠릅니다. 0.0294ms / string v. 0.0399ms / string
7116

확실하게, 사전 할당 유무에 관계없이 StringBuilder를 의미합니까?
Steven Sudit

사전 할당을 사용하면 char [] 할당 및 새 문자열보다 여전히 40 % 느립니다.
user7116

2
나는 이것을 좋아한다. 이 방법을 조정했습니다foreach (char c in input.Where(c => char.IsLetterOrDigit(c) || allowedSpecialCharacters.Any(x => x == c))) buffer[idx++] = c;
Chris Marisic

11

정규식은 다음과 같습니다.

public string RemoveSpecialChars(string input)
{
    return Regex.Replace(input, @"[^0-9a-zA-Z\._]", string.Empty);
}

그러나 성능이 매우 중요한 경우 "정규 경로"를 선택하기 전에 몇 가지 벤치 마크를 수행하는 것이 좋습니다.


11

동적 문자 목록을 사용하는 경우 LINQ는 훨씬 빠르고 우아한 솔루션을 제공 할 수 있습니다.

public static string RemoveSpecialCharacters(string value, char[] specialCharacters)
{
    return new String(value.Except(specialCharacters).ToArray());
}

이 접근 방식을 이전의 "빠른"접근 방법 두 가지 (릴리스 컴파일)와 비교했습니다.

  • LukeH의 문자 배열 솔루션-427ms
  • StringBuilder 솔루션-429ms
  • LINQ (이 답변)-98ms

알고리즘은 약간 수정되었습니다. 문자는 하드 코딩되지 않고 배열로 전달되므로 약간 영향을 줄 수 있습니다 (즉, 다른 솔루션은 문자 배열을 확인하기 위해 내부 foor 루프가 있음).

LINQ where 절을 사용하여 하드 코딩 된 솔루션으로 전환하면 결과는 다음과 같습니다.

  • 문자 배열 솔루션-7ms
  • StringBuilder 솔루션-22ms
  • LINQ-60ms

문자 목록을 하드 코딩하는 대신보다 일반적인 솔루션을 작성하려는 경우 LINQ 또는 수정 된 접근 방식을 살펴볼 가치가 있습니다. LINQ는 Regex보다 간결하고 읽기 쉬운 코드를 제공합니다.


3
이 접근 방식은 멋지지만 작동하지 않습니다. Except ()는 설정된 작업이므로 문자열에서 각 고유 문자의 첫 번째 모양 만 나타납니다.
McKenzieG1

5

알고리즘이 효율적이라고 확신하지는 않습니다. O (n)이며 각 문자를 한 번만 봅니다. 값을 확인하기 전에 마술처럼 값을 알지 않는 한 그 이상을 얻지 못할 것입니다.

그러나 나는 당신의 용량을 StringBuilder문자열의 초기 크기로 초기화 할 것 입니다. 귀하의 인식 성능 문제가 메모리 재 할당에서 비롯된 것 같습니다.

참고 : 점검 A- z안전하지 않습니다. 당신은 포함하고 [, \, ], ^, _,와`...

참고 2 : 효율성을 높이려면 비교 횟수를 최소화하기 위해 비교를 순서대로 수행하십시오. (최악의 경우, 8 개의 비교를 말하고 있으므로 너무 열심히 생각하지 마십시오.) 이것은 예상되는 입력에 따라 변경되지만 한 가지 예는 다음과 같습니다.

if (str[i] >= '0' && str[i] <= 'z' && 
    (str[i] >= 'a' || str[i] <= '9' ||  (str[i] >= 'A' && str[i] <= 'Z') || 
    str[i] == '_') || str[i] == '.')

참고 3 : 어떤 이유에서든 이것이 빠를 필요가 있다면 switch 문이 더 빠를 수 있습니다. 컴파일러는 점프 테이블을 작성하여 단일 비교 만 수행해야합니다.

switch (str[i])
{
    case '0':
    case '1':
    .
    .
    .
    case '.':
        sb.Append(str[i]);
        break;
}

1
나는 이것에 대해 O (n)을 이길 수 없다는 데 동의합니다. 그러나 비교 당 비용이 낮아질 수 있습니다. 테이블 조회에는 고정 비용이 낮고 예외를 추가할수록 일련의 비교에서 비용이 증가합니다.
Steven Sudit

사이드 노트 3에 대해 실제로 점프 테이블이 테이블 조회보다 빠를 것이라고 생각합니까?
Steven Sudit

스위치 솔루션에서 빠른 성능 테스트를 실행했으며 비교와 동일하게 수행됩니다.
Guffa

@Steven Sudit-나는 그들이 거의 동일하다고 벤처 할 것입니다. 시험을 보시겠습니까?
lc.

7
O (n) 표기법은 때때로 나를 화나게합니다. 사람들은 알고리즘이 이미 O (n)이라는 사실에 기초하여 어리석은 가정을 할 것입니다. str [i] 호출을 세계 반대편의 서버와 일회성 SSL 연결을 구성하여 비교 값을 검색하는 함수로 대체하기 위해이 루틴을 변경하면 엄청난 성능을 볼 수 있습니다. 차이와 알고리즘은 STILL O (n)입니다. 각 알고리즘의 O (1) 비용은 중요하며 이에 상응하는 것은 아닙니다!
darron

4
StringBuilder sb = new StringBuilder();

for (int i = 0; i < fName.Length; i++)
{
   if (char.IsLetterOrDigit(fName[i]))
    {
       sb.Append(fName[i]);
    }
}

4

다음과 같이 정규 표현식을 사용할 수 있습니다.

return Regex.Replace(strIn, @"[^\w\.@-]", "", RegexOptions.None, TimeSpan.FromSeconds(1.0));

3

나에게 좋아 보인다. 내가 할 수있는 유일한 개선 StringBuilder은 문자열 길이로 를 초기화하는 것 입니다.

StringBuilder sb = new StringBuilder(str.Length);

3

이 코드 샘플에 동의합니다. 문자열 유형의 확장 메소드로 만드는 유일한 차이점입니다. 매우 간단한 라인이나 코드에서 사용할 수 있습니다.

string test = "abc@#$123";
test.RemoveSpecialCharacters();

실험 해 주셔서 감사합니다.

public static class MethodExtensionHelper
    {
    public static string RemoveSpecialCharacters(this string str)
        {
            StringBuilder sb = new StringBuilder();
            foreach (char c in str)
            {
                if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
                {
                    sb.Append(c);
                }
            }
            return sb.ToString();
        }
}

2

String Replace를 정규 표현식으로 사용하여 "특수 문자"를 검색하고 찾은 모든 문자를 빈 문자열로 바꿉니다.


한 번만 쓰는 정규식을 무시하고 코드를 +1하고 확실하게 더 읽기가 가능합니다.
kenny

1
@kenny-동의합니다. 원래 질문은 심지어 문자열이 10-30 자로 짧다는 것을 나타냅니다. 그러나 분명히 많은 사람들이 여전히 우리가 두 번째 CPU 시간을 팔고 있다고 생각합니다.
Tom Bushell

Reguler expressin은 매우 게 으르므로 항상 사용해서는 안됩니다.
RockOnGom

2

나는 일을 위해 비슷한 것을해야했지만 내 경우에는 문자, 숫자 또는 공백이 아닌 모든 것을 필터링해야했습니다 (그러나 필요에 따라 쉽게 수정할 수 있습니다). 필터링은 JavaScript에서 클라이언트 측에서 수행되지만 보안상의 이유로 필터링 서버 측도 수행하고 있습니다. 대부분의 문자열이 깨끗하다고 ​​기대할 수 있기 때문에 실제로 필요한 경우가 아니면 문자열 복사를 피하고 싶습니다. 이렇게하면 아래 구현을 할 수 있습니다. 깨끗한 문자열과 더러운 문자열 모두에서 더 잘 수행됩니다.

public static string EnsureOnlyLetterDigitOrWhiteSpace(string input)
{
    StringBuilder cleanedInput = null;
    for (var i = 0; i < input.Length; ++i)
    {
        var currentChar = input[i];
        var charIsValid = char.IsLetterOrDigit(currentChar) || char.IsWhiteSpace(currentChar);

        if (charIsValid)
        {
            if(cleanedInput != null)
                cleanedInput.Append(currentChar);
        }
        else
        {
            if (cleanedInput != null) continue;
            cleanedInput = new StringBuilder();
            if (i > 0)
                cleanedInput.Append(input.Substring(0, i));
        }
    }

    return cleanedInput == null ? input : cleanedInput.ToString();
}

1

Linq에 의한 S & G의 경우 :

var original = "(*^%foo)(@)&^@#><>?:\":';=-+_";
var valid = new char[] { 
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 
    'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 
    'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 
    'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', 
    '9', '0', '.', '_' };
var result = string.Join("",
    (from x in original.ToCharArray() 
     where valid.Contains(x) select x.ToString())
        .ToArray());

그러나 이것이 가장 효율적인 방법이라고 생각하지 않습니다.


2
선형 검색이기 때문에 그렇지 않습니다.
Steven Sudit

1
public string RemoveSpecial(string evalstr)
{
StringBuilder finalstr = new StringBuilder();
            foreach(char c in evalstr){
            int charassci = Convert.ToInt16(c);
            if (!(charassci >= 33 && charassci <= 47))// special char ???
             finalstr.append(c);
            }
return finalstr.ToString();
}

1

사용하다:

s.erase(std::remove_if(s.begin(), s.end(), my_predicate), s.end());

bool my_predicate(char c)
{
 return !(isalpha(c) || c=='_' || c==' '); // depending on you definition of special characters
}

그리고 깨끗한 문자열을 얻을 수 s있습니다.

erase()모든 특수 문자를 제거하고 my_predicate()기능을 사용하여 사용자 정의 할 수 있습니다.


1

HashSet이 O (1)
기존 비교보다 빠른지 확실하지 않습니다.

private static HashSet<char> ValidChars = new HashSet<char>() { 'a', 'b', 'c', 'A', 'B', 'C', '1', '2', '3', '_' };
public static string RemoveSpecialCharacters(string str)
{
    StringBuilder sb = new StringBuilder(str.Length / 2);
    foreach (char c in str)
    {
        if (ValidChars.Contains(c)) sb.Append(c);
    }
    return sb.ToString();
}

나는 이것을 받아 들인 대답보다 빠르지 않은 것으로 테스트했다.
구성 가능한 문자 집합이 필요한 것처럼 남겨 두는 것이 좋습니다.


왜 비교가 O (1)이 아니라고 생각합니까?
구파

@ 구파 나는 확실하지 않으며 내 의견을 제거했습니다. 그리고 +1. 의견을 작성하기 전에 더 많은 테스트를 수행 했어야합니다.
paparazzo

1

정규식 기반 대체 (컴파일 가능)가 더 빠른지 궁금합니다. 그것을 테스트해야 할 것누군가가 ~ 5 배 느리다는 것을 .

그 외에도 StringBuilder를 예상 길이로 초기화해야 중간 문자열이 커지는 동안 복사 할 필요가 없습니다.

좋은 숫자는 원래 문자열의 길이이거나 함수 입력의 특성에 따라 약간 더 낮은 것입니다.

마지막으로 조회 테이블 (0..127 범위)을 사용하여 문자를 허용할지 여부를 찾을 수 있습니다.


정규 표현식은 이미 테스트되었으며 약 5 배 느립니다. 0..127 범위의 검색 테이블을 사용하면 문자가 7 비트 값이 아닌 16 비트 값이므로 검색 테이블을 사용하기 전에 문자 코드의 범위를 확인해야합니다.
구파

@Guffa Err ... 네? ;)
Christian Klauser

1

다음 코드에는 다음과 같은 출력이 있습니다 (결론은 배열을 더 작은 크기로 할당하는 일부 메모리 리소스를 절약 할 수 있다는 것입니다).

lookup = new bool[123];

for (var c = '0'; c <= '9'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

for (var c = 'A'; c <= 'Z'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

for (var c = 'a'; c <= 'z'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

48: 0  
49: 1  
50: 2  
51: 3  
52: 4  
53: 5  
54: 6  
55: 7  
56: 8  
57: 9  
65: A  
66: B  
67: C  
68: D  
69: E  
70: F  
71: G  
72: H  
73: I  
74: J  
75: K  
76: L  
77: M  
78: N  
79: O  
80: P  
81: Q  
82: R  
83: S  
84: T  
85: U  
86: V  
87: W  
88: X  
89: Y  
90: Z  
97: a  
98: b  
99: c  
100: d  
101: e  
102: f  
103: g  
104: h  
105: i  
106: j  
107: k  
108: l  
109: m  
110: n  
111: o  
112: p  
113: q  
114: r  
115: s  
116: t  
117: u  
118: v  
119: w  
120: x  
121: y  
122: z  

러시아어 로캘을 지원하기 위해 다음 코드 줄을 추가 할 수도 있습니다 (배열 크기는 1104입니다).

for (var c = 'А'; c <= 'Я'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

for (var c = 'а'; c <= 'я'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

1

가장 효율적인 방법인지는 모르겠지만 저에게 효과적입니다.

 Public Function RemoverTildes(stIn As String) As String
    Dim stFormD As String = stIn.Normalize(NormalizationForm.FormD)
    Dim sb As New StringBuilder()

    For ich As Integer = 0 To stFormD.Length - 1
        Dim uc As UnicodeCategory = CharUnicodeInfo.GetUnicodeCategory(stFormD(ich))
        If uc <> UnicodeCategory.NonSpacingMark Then
            sb.Append(stFormD(ich))
        End If
    Next
    Return (sb.ToString().Normalize(NormalizationForm.FormC))
End Function

대답 효과가 있지만 질문은 C #입니다. (PS : 나는 이것이 실제로 5 년 전인 것을 알고 있지만 여전히 ..) Telerik VB to C # Converter를 사용했고 그 반대의 경우에도 코드는 정상적으로 작동했습니다. (또 다른 것, converter.telerik.com )
Momoro

1

여기에 제안 된 솔루션이 많이 있지만 다른 솔루션보다 효율적이지만 읽기 쉽지는 않습니다. Linq를 활용하여 가장 효율적이지는 않지만 대부분의 상황에서 사용할 수 있으며 간결하고 읽기 쉬운 것이 있습니다.

string stringToclean = "This is a test.  Do not try this at home; you might get hurt. Don't believe it?";

var validPunctuation = new HashSet<char>(". -");

var cleanedVersion = new String(stringToclean.Where(x => (x >= 'A' && x <= 'Z') || (x >= 'a' && x <= 'z') || validPunctuation.Contains(x)).ToArray());

var cleanedLowercaseVersion = new String(stringToclean.ToLower().Where(x => (x >= 'a' && x <= 'z') || validPunctuation.Contains(x)).ToArray());

-1
public static string RemoveSpecialCharacters(string str){
    return str.replaceAll("[^A-Za-z0-9_\\\\.]", "");
}

1
난 두려워 replaceAllC # 문자열 기능하지 않고, 자바 나 자바 스크립트 중
사바 토스

-1
public static string RemoveAllSpecialCharacters(this string text) {
  if (string.IsNullOrEmpty(text))
    return text;

  string result = Regex.Replace(text, "[:!@#$%^&*()}{|\":?><\\[\\]\\;'/.,~]", " ");
  return result;
}

대답이 잘못되었습니다. 정규식을 사용하려는 경우 독점 문자가 아닌 포괄적이어야합니다. 지금 일부 문자가 누락 되었기 때문입니다. 실제로는 이미 정규식에 대한 답변이 있습니다. 그리고 정규식은 정규 표현식이 느리고 직접 문자 비교 기능입니다.
TPAKTOPA

-3

속도가 걱정된다면 포인터를 사용하여 기존 문자열을 편집하십시오. 문자열을 고정하고 포인터를 얻은 다음 각 문자에 대해 for 루프를 실행하여 유효하지 않은 각 문자를 대체 문자로 덮어 씁니다. 매우 효율적이며 새로운 문자열 메모리를 할당 할 필요가 없습니다. unsafe 옵션으로 모듈을 컴파일하고 포인터를 사용하기 위해 "unsafe"수정자를 메소드 헤더에 추가해야합니다.

static void Main(string[] args)
{
    string str = "string!$%with^&*invalid!!characters";
    Console.WriteLine( str ); //print original string
    FixMyString( str, ' ' );
    Console.WriteLine( str ); //print string again to verify that it has been modified
    Console.ReadLine(); //pause to leave command prompt open
}


public static unsafe void FixMyString( string str, char replacement_char )
{
    fixed (char* p_str = str)
    {
        char* c = p_str; //temp pointer, since p_str is read-only
        for (int i = 0; i < str.Length; i++, c++) //loop through each character in string, advancing the character pointer as well
            if (!IsValidChar(*c)) //check whether the current character is invalid
                (*c) = replacement_char; //overwrite character in existing string with replacement character
    }
}

public static bool IsValidChar( char c )
{
    return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '.' || c == '_');
    //return char.IsLetterOrDigit( c ) || c == '.' || c == '_'; //this may work as well
}

14
안돼! .NET에서 문자열을 변경하는 것은 BAAAAAAAAAAAAD입니다! 프레임 워크의 모든 것은 문자열을 변경할 수 없다는 규칙에 의존하며,이를 깨면 매우 놀라운 부작용을 얻을 수 있습니다 ...
Guffa
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.