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


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.