문자열에서 특수 문자를 제거하는 가장 효율적인 방법
문자열에서 모든 특수 문자를 제거합니다.사용할 수 있는 문자는 A-Z(대문자 또는 소문자), 숫자(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.1ms.
StringBuilder 파일: 43.3ms.
정규식: 294.4 ms.
Edit 2: 위 코드에 A-Z와 a-z의 구분을 추가하였습니다. (성능 테스트를 다시 해보니 눈에 띄는 차이가 없습니다.)
3편집 3:
약 lookup+char[]로됩니다.
당연히 지불해야 할 대가는 거대한 룩업 테이블의 초기화와 메모리에 저장하는 것입니다.뭐, 그렇게 많은 데이터는 아니지만, 그런 사소한 기능을 위해서라면...
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);
}
정규식은 다음과 같습니다.
public string RemoveSpecialChars(string input)
{
return Regex.Replace(input, @"[^0-9a-zA-Z\._]", string.Empty);
}
하지만 성능이 매우 중요한 경우에는 "regex 경로"를 선택하기 전에 몇 가지 벤치마크를 수행하는 것이 좋습니다.
정적 생성자에서 초기화하여 임의의 문자 조합을 유효하게 설정할 수 있는 간단한 조회 테이블을 만드는 것이 좋습니다.이렇게 하면 한 번의 빠른 검사를 수행할 수 있습니다.
편집을
또한 속도를 위해 StringBuilder의 용량을 입력 문자열 길이로 초기화할 수 있습니다.이렇게 하면 재할당이 방지됩니다.이 두 가지 방법을 함께 사용하면 속도와 유연성을 모두 얻을 수 있습니다.
또 다른 편집
컴파일러가 최적화할 수도 있다고 생각하지만, 효율성뿐만 아니라 스타일의 문제로, 각각의 것을 추천합니다.
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);
}
동적 문자 목록을 사용하는 경우 LINQ는 훨씬 빠르고 우아한 솔루션을 제공할 수 있습니다.
public static string RemoveSpecialCharacters(string value, char[] specialCharacters)
{
return new String(value.Except(specialCharacters).ToArray());
}
저는 이 접근 방식을 이전의 두 가지 "빠른" 접근 방식(릴리스 컴파일)과 비교했습니다.
- LukeH의 문자 배열 솔루션 - 427ms
- StringBuilder 솔루션 - 429ms
- LINQ(이 답변) - 98ms
알고리즘이 약간 수정되었습니다. 문자는 하드 코딩이 아닌 배열로 전달되어 약간의 영향을 미칠 수 있습니다(즉, 다른 솔루션은 문자 배열을 확인하기 위해 내부 포어 루프가 있습니다).
LINQ where 절을 사용하여 하드 코딩된 솔루션으로 전환하면 다음과 같은 결과가 나타납니다.
- Char 어레이 솔루션 - 7ms
- StringBuilder 솔루션 - 22ms
- LINQ - 60ms
문자 목록을 하드 코딩하는 대신 보다 일반적인 솔루션을 작성할 계획이라면 LINQ 또는 수정된 접근 방식을 살펴볼 가치가 있습니다.LINQ는 Regex보다 훨씬 더 간결하고 읽기 쉬운 코드를 제공합니다.
저는 당신의 알고리즘이 효율적이라고 확신하지 못합니다.O(n)이고 각 문자를 한 번만 봅니다.값을 확인하기 전에 마법처럼 알지 않으면 그보다 더 나아지지 않을 것입니다.
의 할 것입니다.StringBuilder문자열의 초기 크기까지.당신의 인식된 성능 문제는 메모리 재할당에서 기인하는 것 같습니다.
참고 사항:확인 중A-z안전하지 않습니다.▁you▁를 포함하고 있습니다.[,\,],^,_그리고 '...
사이드 노트 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 (str[i])
{
case '0':
case '1':
.
.
.
case '.':
sb.Append(str[i]);
break;
}
다음과 같이 정규식을 사용할 수 있습니다.
return Regex.Replace(strIn, @"[^\w\.@-]", "", RegexOptions.None, TimeSpan.FromSeconds(1.0));
제가 보기엔 좋은 것 같아요.내가 유일하게 개선할 수 있는 것은 초기화하는 것입니다.StringBuilder끈의 길이와 함께.
StringBuilder sb = new StringBuilder(str.Length);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < fName.Length; i++)
{
if (char.IsLetterOrDigit(fName[i]))
{
sb.Append(fName[i]);
}
}
저는 이 코드 샘플에 동의합니다.문자열 유형의 확장 메서드로 만드는 유일한 차이점입니다.매우 간단한 줄이나 코드로 사용할 수 있습니다.
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();
}
}
여기에는 다른 솔루션보다 효율적인 솔루션이 많이 제안되어 있지만, 읽을 수 있는 솔루션은 많지 않을 수도 있습니다.다음은 가장 효율적이지는 않지만 대부분의 상황에서 사용할 수 있으며, 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());
특히 이 기능을 여러 번 호출하는 경우 할당을 줄임으로써 성능을 향상시키는 또 다른 방법입니다.
결과가 입력보다 길지 않음을 보장할 수 있기 때문에 메모리에 추가 복사본을 만들지 않고 입력 및 출력을 전달할 수 있습니다.은 이사수없다니습할용이로를 사용할 수 .stackalloc버퍼 배열을 만들려면 버퍼에서 복사해야 합니다.
public static string RemoveSpecialCharacters(this string str)
{
return RemoveSpecialCharacters(str.AsSpan()).ToString();
}
public static ReadOnlySpan<char> RemoveSpecialCharacters(this ReadOnlySpan<char> str)
{
Span<char> buffer = new char[str.Length];
int idx = 0;
foreach (char c in str)
{
if (char.IsLetterOrDigit(c))
{
buffer[idx] = c;
idx++;
}
}
return buffer.Slice(0, idx);
}
"특수 문자"를 검색하는 정규식으로 문자열 바꾸기를 사용하여 찾은 모든 문자를 빈 문자열로 바꿉니다.
저는 일을 위해 비슷한 일을 해야 했지만, 저의 경우 문자, 숫자 또는 공백이 아닌 모든 것을 필터링해야 했습니다(하지만 필요에 따라 쉽게 수정할 수 있습니다).자바스크립트에서 필터링은 클라이언트 측에서 수행되지만 보안상의 이유로 저도 필터링 서버 측에서 수행하고 있습니다.대부분의 문자열이 깨끗할 것으로 예상할 수 있기 때문에 굳이 문자열을 복사할 필요가 없는 한 복사를 피하고 싶습니다.이렇게 하면 깨끗한 문자열과 더러운 문자열 모두에서 더 나은 성능을 발휘할 수 있는 아래 구현을 수행할 수 있습니다.
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();
}
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());
하지만 저는 이것이 가장 효율적인 방법이라고 생각하지 않습니다.
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();
}
사용:
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()기능.
해시 집합은 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();
}
저는 테스트를 했고 이것은 승인된 답변보다 빠르지 않습니다.
구성 가능한 문자 집합이 필요한 것처럼 남겨두겠습니다. 이것이 좋은 해결책이 될 것입니다.
Regex 기반 교체(아마 컴파일됨)가 더 빠른지 궁금합니다.
그것을 테스트해야 할 것입니다. 누군가가 이것이 ~5배 더 느리다는 것을 발견했습니다.
그 외에는 StringBuilder를 예상 길이로 초기화하여 중간 문자열을 확장하는 동안 복사할 필요가 없습니다.
올바른 숫자는 원래 문자열의 길이 또는 함수 입력의 특성에 따라 약간 더 낮은 길이입니다.
마지막으로 룩업 테이블(0..127 범위)을 사용하여 문자를 허용할지 여부를 확인할 수 있습니다.
다음 코드의 출력은 다음과 같습니다(결론적으로 어레이를 더 작은 크기로 할당하는 일부 메모리 리소스도 저장할 수 있습니다).
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);
}
그게 가장 효율적인 방법인지는 모르겠지만, 저에게는 효과가 있습니다.
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
가장 짧은 길은 3줄...
public static string RemoveSpecialCharacters(string str)
{
var sb = new StringBuilder();
foreach (var c in str.Where(c => c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z' || c == '.' || c == '_')) sb.Append(c);
return sb.ToString();
}
간단한 방법LINQ
string text = "123a22 ";
var newText = String.Join(string.Empty, text.Where(x => x != 'a'));
주사나 오타(희귀 이벤트)의 경우 입력 문자열을 정리해야 하는 경우 가장 빠른 방법은switch()모든 문자를 확인합니다. (컴파일러는 실행 시간을 최적화하는 좋은 일을 합니다.)switch()추가 코드를 추가하여 원하지 않는 문자가 발견된 경우 제거합니다.솔루션은 다음과 같습니다.
public static string RemoveExtraCharacters(string input)
{
if (string.IsNullOrEmpty(input))
return "";
input = input.Trim();
StringBuilder sb = null;
reStart:
if (!string.IsNullOrEmpty(input))
{
var len = input.Length; ;
for (int i = 0; i < len; i++)
{
switch (input[i])
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'G':
case 'H':
case 'I':
case 'J':
case 'K':
case 'L':
case 'M':
case 'N':
case 'O':
case 'Q':
case 'P':
case 'R':
case 'S':
case 'T':
case 'U':
case 'V':
case 'W':
case 'X':
case 'Y':
case 'Z':
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
case 'h':
case 'i':
case 'j':
case 'k':
case 'l':
case 'm':
case 'n':
case 'o':
case 'q':
case 'p':
case 'r':
case 's':
case 't':
case 'u':
case 'v':
case 'w':
case 'x':
case 'y':
case 'z':
case '/':
case '_':
case '-':
case '+':
case '.':
case ',':
case '*':
case ':':
case '=':
case ' ':
case '^':
case '$':
break;
default:
if (sb == null)
sb = new StringBuilder();
sb.Append(input.Substring(0, i));
if (i + 1 < len)
{
input = input.Substring(i + 1);
goto reStart;
}
else
input = null;
break;
}
}
}
if (sb != null)
{
if (input != null)
sb.Append(input);
return sb.ToString();
}
return input;
}
public static string RemoveAllSpecialCharacters(this string text) {
if (string.IsNullOrEmpty(text))
return text;
string result = Regex.Replace(text, "[:!@#$%^&*()}{|\":?><\\[\\]\\;'/.,~]", " ");
return result;
}
속도가 걱정되는 경우 포인터를 사용하여 기존 문자열을 편집합니다.문자열을 고정하고 포인터를 가져온 다음 각 문자에 대해 for 루프를 실행하여 잘못된 각 문자를 대체 문자로 덮어쓸 수 있습니다.이는 매우 효율적이며 새 문자열 메모리를 할당할 필요가 없습니다.또한 안전하지 않은 옵션을 사용하여 모듈을 컴파일하고 포인터를 사용하려면 메서드 헤더에 "안전하지 않은" 한정자를 추가해야 합니다.
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
}
public static string RemoveSpecialCharacters(string str){
return str.replaceAll("[^A-Za-z0-9_\\\\.]", "");
}
언급URL : https://stackoverflow.com/questions/1120198/most-efficient-way-to-remove-special-characters-from-string
'code' 카테고리의 다른 글
| Objective-C 네임스페이스 충돌을 해결하는 가장 좋은 방법은 무엇입니까? (0) | 2023.06.02 |
|---|---|
| MVC4 번들의 {version} 와일드카드 (0) | 2023.06.02 |
| 단일 항목을 IEnumberable로 전달 (0) | 2023.06.02 |
| RVM이 있는 Lion 아래에 Ruby를 설치할 수 없음 – GCC 문제 (0) | 2023.06.02 |
| j여러 요소에 대해 동일한 클릭 이벤트 쿼리 (0) | 2023.06.02 |