대소 문자를 구분하지 않는 문자열 대체 방법이 있습니까?
문자열을 검색하고 모든 발생 %FirstName%
과 %PolicyAmount%
데이터베이스에서 가져온 값으로 바꿔야 합니다. 문제는 FirstName의 대소 문자가 다양하다는 것입니다. 그 String.Replace()
방법 을 사용하지 못하게합니다 . 제안하는 주제에 대한 웹 페이지를 보았습니다.
Regex.Replace(strInput, strToken, strReplaceWith, RegexOptions.IgnoreCase);
그러나 나는 시도하고 대체 어떤 이유 %PolicyAmount%
와 함께 $0
, 교체는 발생하지 않았다. 달러 기호가 정규식에서 예약 된 문자와 관련이 있다고 가정합니다.
정규식 특수 문자를 처리하기 위해 입력을 살균하지 않는 다른 방법이 있습니까?
MSDN
$ 0- "그룹 번호 번호 (10 진수)와 일치하는 마지막 하위 문자열을 대체합니다."
.NET 정규식에서 그룹 0은 항상 전체 일치합니다. 리터럴 $의 경우
string value = Regex.Replace("%PolicyAmount%", "%PolicyAmount%", @"$$0", RegexOptions.IgnoreCase);
처럼 보인다 string.Replace
해야 걸리는 과부하가 StringComparison
인수를. 그렇지 않기 때문에 다음과 같이 시도 할 수 있습니다.
public static string ReplaceString(string str, string oldValue, string newValue, StringComparison comparison)
{
StringBuilder sb = new StringBuilder();
int previousIndex = 0;
int index = str.IndexOf(oldValue, comparison);
while (index != -1)
{
sb.Append(str.Substring(previousIndex, index - previousIndex));
sb.Append(newValue);
index += oldValue.Length;
previousIndex = index;
index = str.IndexOf(oldValue, index, comparison);
}
sb.Append(str.Substring(previousIndex));
return sb.ToString();
}
질문의 제목이 실제로 요청되는 특정 질문보다 훨씬 크기 때문에 혼란스러운 답변 그룹입니다 . 읽은 후, 나는 여기에 모든 좋은 것들을 동화시키는 것에 대한 몇 가지 편집 내용이 있는지 확실하지 않으므로 요약하려고합니다.
여기에 언급 된 함정을 피하고 가장 광범위하게 적용 가능한 솔루션을 제공하는 확장 방법이 있습니다.
public static string ReplaceCaseInsensitiveFind(this string str, string findMe,
string newValue)
{
return Regex.Replace(str,
Regex.Escape(findMe),
Regex.Replace(newValue, "\\$[0-9]+", @"$$$0"),
RegexOptions.IgnoreCase);
}
그래서...
- 이것은 확장 방법 @MarkRobinson입니다
- 이것은 Regex @Helge 를 건너 뛰려고하지 않습니다 (Regex 외부 에서이 문자열을 스니핑하려면 실제로 바이트 단위로 수행해야합니다)
- @MichaelLiu의 패스 우수한 테스트 케이스를 ,
"œ".ReplaceCaseInsensitiveFind("oe", "")
그는 마음에 약간 다른 동작을했을 수 있지만.
불행히도, 세 가지 모두에 대한 @HA의 의견 Escape
은 정확하지 않습니다 . 초기 값이며 newValue
반드시 그럴 필요는 없습니다.
참고 : 그러나 "포착 된 값"마커 인 것의 일부인 경우$
삽입하는 새 값 에서을 이스케이프해야합니다 . 따라서 Regex.Replace [sic] 안에있는 Regex.Replace의 3 달러 기호. 그것 없이는 이와 같은 것이 깨집니다.
"This is HIS fork, hIs spoon, hissssssss knife.".ReplaceCaseInsensitiveFind("his", @"he$0r")
오류는 다음과 같습니다.
An unhandled exception of type 'System.ArgumentException' occurred in System.dll
Additional information: parsing "The\hisr\ is\ he\HISr\ fork,\ he\hIsr\ spoon,\ he\hisrsssssss\ knife\." - Unrecognized escape sequence \h.
Regex에 익숙한 사람들은 사용이 오류를 피하는 것처럼 느껴지지만 여전히 스니핑 문자열 (부분적 으로 인코딩에서 Spolsky 를 읽은 후에 만)에서 부분적으로 여전히 당신이 무엇을 얻는 지 확신합니다. 중요한 사용 사례를위한 것입니다. Crockford가 " 안전하지 않은 정규 표현식 "에 대해 조금 생각 나게합니다 . 너무 자주 우리는 우리가 원하는 것을 허용하는 $10
정규 표현식을 작성 하지만 (운이 좋으면) 의도하지 않게 더 많은 것을 허용합니다 (예를 들어 , 위의 newValue 정규 표현식에서 실제로 유효한 "캡처 값"문자열입니까?) . 두 방법 모두 가치가 있으며, 두 가지 방법 모두 서로 다른 유형의 의도하지 않은 오류를 권장합니다. 복잡성을 과소 평가하는 것은 종종 쉬운 일입니다.
그 이상한 $
탈출 (그리고 대체 가치에서 예상했던 Regex.Escape
것처럼 캡처 된 가치 패턴을 피하지 못했습니다 $0
)은 한동안 나를 미치게했습니다. 프로그래밍이 어렵다 (C) 1842
확장 방법은 다음과 같습니다. 어디서 찾았는지 모르겠습니다.
public static class StringExtensions
{
public static string Replace(this string originalString, string oldValue, string newValue, StringComparison comparisonType)
{
int startIndex = 0;
while (true)
{
startIndex = originalString.IndexOf(oldValue, startIndex, comparisonType);
if (startIndex == -1)
break;
originalString = originalString.Substring(0, startIndex) + newValue + originalString.Substring(startIndex + oldValue.Length);
startIndex += newValue.Length;
}
return originalString;
}
}
가장 쉬운 방법은 .Net과 함께 제공되며 .Net 1.0부터 사용 된 Replace 메서드를 사용하는 것입니다.
string res = Microsoft.VisualBasic.Strings.Replace(res,
"%PolicyAmount%",
"$0",
Compare: Microsoft.VisualBasic.CompareMethod.Text);
이 방법을 사용하려면 Microsoft.VisualBasic 어셈블리에 대한 참조를 추가해야합니다. 이 어셈블리는 .Net 런타임의 표준 부분으로, 추가 다운로드가 아니거나 더 이상 사용되지 않는 것으로 표시됩니다.
/// <summary>
/// A case insenstive replace function.
/// </summary>
/// <param name="originalString">The string to examine.(HayStack)</param>
/// <param name="oldValue">The value to replace.(Needle)</param>
/// <param name="newValue">The new value to be inserted</param>
/// <returns>A string</returns>
public static string CaseInsenstiveReplace(string originalString, string oldValue, string newValue)
{
Regex regEx = new Regex(oldValue,
RegexOptions.IgnoreCase | RegexOptions.Multiline);
return regEx.Replace(originalString, newValue);
}
cfeduke의 답변에서 영감을 얻어 IndexOf를 사용하여 문자열에서 이전 값을 찾은 다음 새 값으로 대체하는이 함수를 만들었습니다. 나는 이것을 수백만 행을 처리하는 SSIS 스크립트에서 사용했으며 정규식 방법은 이것보다 느 렸습니다.
public static string ReplaceCaseInsensitive(this string str, string oldValue, string newValue)
{
int prevPos = 0;
string retval = str;
// find the first occurence of oldValue
int pos = retval.IndexOf(oldValue, StringComparison.InvariantCultureIgnoreCase);
while (pos > -1)
{
// remove oldValue from the string
retval = retval.Remove(pos, oldValue.Length);
// insert newValue in it's place
retval = retval.Insert(pos, newValue);
// check if oldValue is found further down
prevPos = pos + newValue.Length;
pos = retval.IndexOf(oldValue, prevPos, StringComparison.InvariantCultureIgnoreCase);
}
return retval;
}
에 확장 C. 드래곤 (76) 의 확장 과부하가 기본으로 자신의 코드를 만들어 '의 인기 대답 Replace
하는 방법.
public static class StringExtensions
{
public static string Replace(this string str, string oldValue, string newValue, StringComparison comparison)
{
StringBuilder sb = new StringBuilder();
int previousIndex = 0;
int index = str.IndexOf(oldValue, comparison);
while (index != -1)
{
sb.Append(str.Substring(previousIndex, index - previousIndex));
sb.Append(newValue);
index += oldValue.Length;
previousIndex = index;
index = str.IndexOf(oldValue, index, comparison);
}
sb.Append(str.Substring(previousIndex));
return sb.ToString();
}
}
Jeff Reddy의 답변과 일부 최적화 및 검증을 기반으로합니다.
public static string Replace(string str, string oldValue, string newValue, StringComparison comparison)
{
if (oldValue == null)
throw new ArgumentNullException("oldValue");
if (oldValue.Length == 0)
throw new ArgumentException("String cannot be of zero length.", "oldValue");
StringBuilder sb = null;
int startIndex = 0;
int foundIndex = str.IndexOf(oldValue, comparison);
while (foundIndex != -1)
{
if (sb == null)
sb = new StringBuilder(str.Length + (newValue != null ? Math.Max(0, 5 * (newValue.Length - oldValue.Length)) : 0));
sb.Append(str, startIndex, foundIndex - startIndex);
sb.Append(newValue);
startIndex = foundIndex + oldValue.Length;
foundIndex = str.IndexOf(oldValue, startIndex, comparison);
}
if (startIndex == 0)
return str;
sb.Append(str, startIndex, str.Length - startIndex);
return sb.ToString();
}
C. Dragon과 유사한 버전이지만 단일 교체 만 필요한 경우 :
int n = myText.IndexOf(oldValue, System.StringComparison.InvariantCultureIgnoreCase);
if (n >= 0)
{
myText = myText.Substring(0, n)
+ newValue
+ myText.Substring(n + oldValue.Length);
}
정규 표현식 대체를 실행하는 또 다른 옵션은 다음과 같습니다. 일치하는 문자열에 위치가 포함되어있는 사람은 많지 않습니다.
public static string ReplaceCaseInsensative( this string s, string oldValue, string newValue ) {
var sb = new StringBuilder(s);
int offset = oldValue.Length - newValue.Length;
int matchNo = 0;
foreach (Match match in Regex.Matches(s, Regex.Escape(oldValue), RegexOptions.IgnoreCase))
{
sb.Remove(match.Index - (offset * matchNo), match.Length).Insert(match.Index - (offset * matchNo), newValue);
matchNo++;
}
return sb.ToString();
}
Regex.Replace(strInput, strToken.Replace("$", "[$]"), strReplaceWith, RegexOptions.IgnoreCase);
정규식 방법이 작동해야합니다. 그러나 수행 할 수있는 작업은 데이터베이스의 소문자 문자열, 소문자의 % variables %를 소문자로 한 다음 데이터베이스의 소문자 문자열에서 위치와 길이를 찾는 것입니다. 문자열의 위치는 소문자이므로 변경되지 않습니다.
그런 다음 역순으로 돌아가는 루프를 사용하여 (나중에 포인트가 이동하는 곳의 실행 횟수를 유지하지 않아도되는 경우 더 쉬움) 데이터베이스에서 소문자가 아닌 문자열을 % variables % 위치에서 제거하십시오. 길이를 바꾸고 교체 값을 삽입하십시오.
(모두가 총에 맞기 때문에). 다음은 내 버전입니다 (널 체크 및 올바른 입력 및 교체 이스케이프 포함) ** 인터넷 및 기타 버전에서 영감을 얻었습니다.
using System;
using System.Text.RegularExpressions;
public static class MyExtensions {
public static string ReplaceIgnoreCase(this string search, string find, string replace) {
return Regex.Replace(search ?? "", Regex.Escape(find ?? ""), (replace ?? "").Replace("$", "$$"), RegexOptions.IgnoreCase);
}
}
용법:
var result = "This is a test".ReplaceIgnoreCase("IS", "was");
내가 사건을 제기하면 원한다면 파쇄로 나를 찢을 수 있습니다.
Regex는이 문제에 대한 답이 아닙니다. 너무 느리고 메모리가 부족하여 상대적으로 말하십시오.
StringBuilder는 문자열 맹 글링보다 훨씬 좋습니다.
이것이 보완하는 확장 방법이므로 string.Replace
, 그것이 작동하는 방식과 일치하는 것이 중요하다고 생각합니다. 따라서 동일한 인수 문제에 대해 예외를 던지는 것이 교체하지 않은 경우 원래 문자열을 반환하는 것과 마찬가지로 중요합니다.
StringComparison 매개 변수를 갖는 것은 좋은 생각이 아니라고 생각합니다. 나는 그것을 시도했지만 michael-liu가 처음 언급 한 테스트 사례에 문제가 있음을 보여주었습니다.
[TestCase("œ", "oe", "", StringComparison.InvariantCultureIgnoreCase, Result = "")]
IndexOf가 일치하는 동안 소스 문자열 (1)의 일치 길이와 oldValue.Length (2)가 일치하지 않습니다. oldValue.Length가 현재 일치 위치에 추가되었을 때 다른 솔루션에서 IndexOutOfRange가 발생하여 나타납니다. 어쨌든 정규식은 사례와 일치하지 않으므로 솔루션에 대해서만 실용적인 솔루션을 사용 StringComparison.OrdinalIgnoreCase
했습니다.
내 코드는 다른 답변과 비슷하지만 내 트위스트는을 만드는 데 어려움을 겪기 전에 일치하는 것을 찾는 것 StringBuilder
입니다. 아무것도 발견되지 않으면 잠재적으로 큰 할당을 피할 수 있습니다. 그런 다음 코드 do{...}while
는while{...}
나는 다른 답변에 대해 광범위한 테스트를 수행했으며 이것은 조금 더 빠르며 약간 적은 메모리를 사용했습니다.
public static string ReplaceCaseInsensitive(this string str, string oldValue, string newValue)
{
if (str == null) throw new ArgumentNullException(nameof(str));
if (oldValue == null) throw new ArgumentNullException(nameof(oldValue));
if (oldValue.Length == 0) throw new ArgumentException("String cannot be of zero length.", nameof(oldValue));
var position = str.IndexOf(oldValue, 0, StringComparison.OrdinalIgnoreCase);
if (position == -1) return str;
var sb = new StringBuilder(str.Length);
var lastPosition = 0;
do
{
sb.Append(str, lastPosition, position - lastPosition);
sb.Append(newValue);
} while ((position = str.IndexOf(oldValue, lastPosition = position + oldValue.Length, StringComparison.OrdinalIgnoreCase)) != -1);
sb.Append(str, lastPosition, str.Length - lastPosition);
return sb.ToString();
}
'Programing' 카테고리의 다른 글
텍스트 파일을 한 줄씩 읽는 가장 빠른 방법은 무엇입니까? (0) | 2020.03.15 |
---|---|
신뢰할 수없는 인증서에 대해 NSURLConnection을 사용하여 SSL에 연결하는 방법은 무엇입니까? (0) | 2020.03.15 |
클래스 패스 란 무엇이며 어떻게 설정합니까? (0) | 2020.03.15 |
Django가있는 AngularJS-템플릿 태그 충돌 (0) | 2020.03.15 |
nativeGetEnabledTags에서 예기치 않은 값 : 0 (0) | 2020.03.15 |