문자열에서 모든 특수 문자를 제거하고 싶습니다. 허용되는 문자는 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 자 정도의 짧은 길이입니다.
답변
왜 당신의 방법이 비효율적이라고 생각합니까? 실제로 가장 효율적인 방법 중 하나입니다.
물론 문자를 로컬 변수로 읽거나 열거자를 사용하여 배열 액세스 수를 줄여야합니다.
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);
}
답변
글쎄, 당신이 정말로 당신의 기능에서 성능을 짜낼 필요가 없다면, 유지하고 이해하기 가장 쉬운 것을 따라 가십시오. 정규식은 다음과 같습니다.
추가 성능을 위해 사전 컴파일하거나 첫 번째 호출에서 컴파일하도록 지시 할 수 있습니다 (이후의 호출은 더 빠릅니다).
public static string RemoveSpecialCharacters(string str)
{
return Regex.Replace(str, "[^a-zA-Z0-9_.]+", "", RegexOptions.Compiled);
}
답변
간단한 조회 테이블을 만드는 것이 좋습니다.이 테이블은 정적 생성자에서 초기화하여 문자 조합을 유효하게 설정할 수 있습니다. 이를 통해 빠른 단일 검사를 수행 할 수 있습니다.
편집하다
또한 속도를 높이기 위해 StringBuilder의 용량을 입력 문자열의 길이로 초기화하려고합니다. 재 할당을 피할 수 있습니다. 이 두 가지 방법을 함께 사용하면 속도와 유연성이 모두 향상됩니다.
다른 편집
컴파일러가 최적화 할 수 있다고 생각하지만 효율성뿐만 아니라 스타일 측면에서도 foreach를 권장합니다.
답변
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);
}
답변
정규식은 다음과 같습니다.
public string RemoveSpecialChars(string input)
{
return Regex.Replace(input, @"[^0-9a-zA-Z\._]", string.Empty);
}
그러나 성능이 매우 중요한 경우 “정규 경로”를 선택하기 전에 몇 가지 벤치 마크를 수행하는 것이 좋습니다.
답변
동적 문자 목록을 사용하는 경우 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보다 간결하고 읽기 쉬운 코드를 제공합니다.
답변
알고리즘이 효율적이라고 확신하지는 않습니다. 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;
}