क्या स्ट्रिंग का एक विकल्प है। क्या यह असंवेदनशील है?


306

मुझे एक स्ट्रिंग को खोजने %FirstName%और %PolicyAmount%डेटाबेस से खींचे गए मान के साथ सभी घटनाओं को बदलने की आवश्यकता है । समस्या यह है कि FirstName का कैपिटलाइज़ेशन भिन्न होता है। जो मुझे String.Replace()विधि का उपयोग करने से रोकता है । मैंने सुझाव देने वाले विषय पर वेब पृष्ठ देखे हैं

Regex.Replace(strInput, strToken, strReplaceWith, RegexOptions.IgnoreCase);

हालांकि किसी कारण जब मैं कोशिश करते हैं और बदलने के लिए %PolicyAmount%के साथ $0, प्रतिस्थापन कभी नहीं होता है। मुझे लगता है कि यह कुछ करने के लिए डॉलर के हस्ताक्षर के साथ एक आरक्षित चरित्र regex में किया जा रहा है।

वहाँ एक और तरीका है कि मैं उपयोग कर सकते हैं कि regex विशेष पात्रों से निपटने के लिए इनपुट sanitizing शामिल नहीं है?


1
यदि "$ 0" वैरिएबल है, जो रेगेक्स को बिल्कुल प्रभावित नहीं करता है।
cfeduke

जवाबों:


132

MSDN
$ 0 से - "समूह नंबर संख्या (दशमलव) द्वारा मिलान किए गए अंतिम विकल्प को सब्सक्राइब करें।"

.NET में रेग्युलर एक्सप्रेशन ग्रुप 0 हमेशा पूरे मैच होते हैं। एक शाब्दिक $ के लिए आप की जरूरत है

string value = Regex.Replace("%PolicyAmount%", "%PolicyAmount%", @"$$0", RegexOptions.IgnoreCase);

16
इस विशेष मामले में यह ठीक है, लेकिन उन मामलों में जहां तार बाहर से इनपुट होते हैं, कोई यह सुनिश्चित नहीं कर सकता है कि वे वर्ण नहीं हैं जो नियमित अभिव्यक्तियों में कुछ खास मतलब रखते हैं
एलनबरो

23
आपको इस तरह के विशेष पात्रों से बचना चाहिए: स्ट्रिंग मान = Regex.Replace ("% PolicyAmount%", Regex.Escape ("% PolicyAmount%"), Regex.Escape ("$ 0"), RegexOptions.IgnoreCase);
हेल्ज क्लेन

8
Regex.Replace में Regex.Escape का उपयोग करते समय कृपया देखें। आपको पास किए गए तीनों तारों से बचना होगा और परिणाम पर Regex.Unescape कॉल करना होगा!
होल्गर एडम

4
Msdn के अनुसार: "चरित्र के पलायन को नियमित अभिव्यक्ति पैटर्न में पहचाना जाता है लेकिन प्रतिस्थापन पैटर्न में नहीं।" ( Msdn.microsoft.com/en-us/library/4edbef7e.aspx )
Bronek

1
यह उपयोग करने के लिए सबसे अच्छा है: स्ट्रिंग मान = Regex.Replace ("% PolicyAmount%", Regex.Escape ("% PolicyAmount%"), "$ 0" .Replace ("$", "$ $"), RegexOptions.IgnoreCase); के रूप में प्रतिस्थापन केवल dolar संकेतों को पहचानता है।
स्कोर्क

295

ऐसा लगता है कि एक अधिभार 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();
}

9
अच्छा लगा। मैं बदल जाएगा ReplaceStringकरने के लिए Replace
एमीसिको जूल

41
उपरोक्त टिप्पणियों से सहमत हों। यह एक ही विधि नाम के साथ एक विस्तार विधि में बनाया जा सकता है। बस इसे विधि हस्ताक्षर के साथ एक स्थिर वर्ग में पॉप करें: सार्वजनिक स्थैतिक स्ट्रिंग बदलें (यह स्ट्रिंग str, स्ट्रिंग oldValue, स्ट्रिंग newValue, StringComparison तुलना)
मार्क रॉबिन्सन

8
@ सामान्य रूप से, यह ठीक हो सकता है, लेकिन मुझे उपयोगकर्ता से मनमाना तार लेना होगा और इनपुट को regex के सार्थक होने का जोखिम नहीं उठा सकता। बेशक, मुझे लगता है कि मैं एक लूप लिख सकता हूं और प्रत्येक चरित्र के सामने एक बैकस्लैश डाल सकता हूं ... उस बिंदु पर, मैं उपरोक्त (आईएमएचओ) भी कर सकता हूं।
जिम

9
इकाई परीक्षण के दौरान मैं इस मामले में भाग गया, जहां यह कभी नहीं लौटेगा oldValue == newValue == ""
इस्माइल

10
यह छोटी गाड़ी है; ReplaceString("œ", "oe", "", StringComparison.InvariantCulture)फेंकता है ArgumentOutOfRangeException
माइकल लियू

45

उत्तर में एक भ्रामक समूह की तरह, भाग में क्योंकि प्रश्न का शीर्षक वास्तव में पूछे जाने वाले विशिष्ट प्रश्न से बहुत बड़ा है। के माध्यम से पढ़ने के बाद, मुझे यकीन नहीं है कि कोई भी उत्तर कुछ अच्छा सामान यहां से आत्मसात करने से दूर है, इसलिए मुझे लगा कि मैं योग करने की कोशिश करूंगा।

यहाँ एक विस्तार विधि है जो मुझे लगता है कि यहाँ बताए गए नुकसान से बचा जाता है और सबसे व्यापक रूप से लागू समाधान प्रदान करता है।

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);
}

इसलिए...

दुर्भाग्य से, @HA की टिप्पणी है कि आप Escapeतीनों के लिए सही नहीं है । प्रारंभिक मूल्य और newValueहोने की जरूरत नहीं है।

नोट: आप, हालांकि, $आपके द्वारा सम्मिलित किए जा रहे नए मान से बचने के लिए हैं यदि वे "कैप्चर किए गए मूल्य" मार्कर के रूप में दिखाई देंगे । इस प्रकार Regex.Replace के अंदर Regex.Replace में तीन डॉलर के संकेत हैं। [sic]। उसके बिना, इस तरह कुछ टूट जाता है ...

"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.

आपको बताता हूं कि, मुझे पता है कि रेगेक्स के साथ सहज महसूस करने वाले लोग अपने उपयोग की त्रुटियों से बचते हैं, लेकिन मैं अक्सर अभी भी आंशिक रूप से बाइट सूँघने के तार (लेकिन केवल एन्कोडिंग पर स्पोलस्की पढ़ने के बाद ) के लिए आंशिक रूप से निश्चित हूं कि आपको क्या मिल रहा है महत्वपूर्ण उपयोग के मामलों के लिए इरादा। क्रॉकफोर्ड की याद दिलाता है " असुरक्षित नियमित अभिव्यक्ति " पर थोड़ा। बहुत बार हम regexps लिखते हैं जो हम चाहते हैं (यदि हम भाग्यशाली हैं) की अनुमति देते हैं, लेकिन अनजाने में अधिक की अनुमति देते हैं (जैसे:$10 मेरे newValue regexp, (?) में वास्तव में एक वैध "कैप्चर मान" स्ट्रिंग है?) क्योंकि हम विचारशील पर्याप्त थे? । दोनों विधियों में मूल्य हैं, और दोनों विभिन्न प्रकार की अनजाने त्रुटियों को प्रोत्साहित करते हैं। अक्सर जटिलता को कम करके समझना आसान होता है।

यह अजीब $भागने (और जो Regex.Escapeकैप्चर किए गए मूल्य पैटर्न से बच नहीं पाया था जैसे $0कि मैं प्रतिस्थापन मूल्यों में उम्मीद कर रहा था) ने मुझे थोड़ी देर के लिए पागल कर दिया। प्रोग्रामिंग इज़ हार्ड (c) 1842


32

यहाँ एक विस्तार विधि है। यकीन नहीं होता कि मुझे यह कहां मिला।

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;
    }

}

आपको खाली / अशक्त स्ट्रिंग मामलों को संभालने की आवश्यकता हो सकती है।
वड

2
इस समाधान में म्यूटेंट की त्रुटियां: 1. मूल स्ट्रिंग, पुरानेवैल्यू और न्यूवैल्यू को शून्य के लिए जांचें। 2. ऑर्गेनाइलस्ट्रिंग को वापस न दें (काम नहीं करता है, सरल प्रकार संदर्भ द्वारा पारित नहीं होते हैं), लेकिन ऑर्गेनिवलव्यू के मूल्य को पहले एक नए स्ट्रिंग में असाइन करें और इसे संशोधित करें और इसे वापस दें।
RWC

31

लगता है कि सबसे आसान तरीका है कि बदलें विधि का उपयोग करें कि .Net के साथ जहाज है और .Net 1.0 के बाद से आसपास है।

string res = Microsoft.VisualBasic.Strings.Replace(res, 
                                   "%PolicyAmount%", 
                                   "$0", 
                                   Compare: Microsoft.VisualBasic.CompareMethod.Text);

इस विधि का उपयोग करने के लिए, आपको Microsoft.VisualBasic को असेंबल करना होगा। यह असेंबली .Net रनटाइम का एक मानक हिस्सा है, यह अतिरिक्त डाउनलोड नहीं है या अप्रचलित के रूप में चिह्नित है।


4
यह काम करता हैं। आपको Microsoft.VisualBasic असेंबली के लिए एक संदर्भ जोड़ना होगा।
क्लीवरपैट्रिक

अजीब बात है कि इस पद्धति में कुछ समस्याएं थीं जब मैंने इसका इस्तेमाल किया था (लाइन की शुरुआत में पात्र गायब हो गए थे)। यहाँ से सबसे लोकप्रिय जवाब C. Dragon 76उम्मीद के मुताबिक काम किया।
जेरेमी थॉम्पसन

1
इसके साथ समस्या यह है कि यदि कोई प्रतिस्थापन नहीं किया जाता है, तो भी एक नया स्ट्रिंग लौटाता है, जहाँ string.replace () उसी स्ट्रिंग के लिए एक पॉइंटर लौटाता है। अक्षम हो सकते हैं यदि आप एक फार्म पत्र मर्ज की तरह कुछ कर रहे हैं।
ब्रेन2000

4
Brain2000, आप गलत हैं। .NET में सभी स्ट्रिंग अपरिवर्तनीय हैं।
डेर_मिस्टर

Der_Meister, जबकि आप जो कहते हैं वह सही है, यह ब्रेन2000 ने गलत नहीं कहा है।
सिमोन हेविट

11
    /// <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);
    }

बेहतर तरीका कौन सा है? क्या stackoverflow.com/a/244933/206730 के बारे में है ? बेहतर प्रदर्शन?
किकेनेट

8

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;
}

+1 आवश्यक नहीं होने पर रेगेक्स का उपयोग नहीं करने के लिए। ज़रूर, आप कोड की कुछ और पंक्तियों का उपयोग करते हैं, लेकिन रेगेक्स-आधारित प्रतिस्थापन की तुलना में इसकी अधिक कुशल है जब तक कि आपको $ कार्यक्षमता की आवश्यकता न हो।
क्रिस जी

6

पर विस्तार सी ड्रैगन 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();
     }
}

3

जेफ रेड्डी के जवाब के आधार पर, कुछ अनुकूलन और सत्यापन के साथ:

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();
}

2

सी। ड्रैगन के समान एक संस्करण, लेकिन अगर आपको केवल एक ही प्रतिस्थापन की आवश्यकता है:

int n = myText.IndexOf(oldValue, System.StringComparison.InvariantCultureIgnoreCase);
if (n >= 0)
{
    myText = myText.Substring(0, n)
        + newValue
        + myText.Substring(n + oldValue.Length);
}

1

रेगेक्स रिप्लेसमेंट को निष्पादित करने के लिए यहां एक और विकल्प है, क्योंकि बहुत से लोग नोटिस नहीं करते हैं कि मैच में स्ट्रिंग के भीतर स्थान होता है:

    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();
    }

क्या आप बता सकते हैं कि आप मिलानो द्वारा गुणा क्यों कर रहे हैं?
ऐहो

यदि OldValue और NewValue के बीच की लंबाई में अंतर है, तो मानों को बदलने के साथ ही स्ट्रिंग लंबी या छोटी हो जाएगी। मैच। इंडेक्स स्ट्रिंग के भीतर मूल स्थान को संदर्भित करता है, हमें अपने प्रतिस्थापन के कारण उस स्थिति के आंदोलन के लिए समायोजित करने की आवश्यकता है। एक और तरीका यह होगा कि आप दायीं ओर से बायीं ओर निकालें / डालें पर अमल करें।
ब्रैंडन

मै समझ गया। यही "ऑफसेट" चर के लिए है। मुझे समझ नहीं आ रहा है कि आप माचिस से क्यों गुणा कर रहे हैं। मेरा अंतर्ज्ञान मुझे बताता है कि एक स्ट्रिंग के भीतर मैच के स्थान का पिछली घटनाओं की वास्तविक गणना से कोई संबंध नहीं होगा।
अन्हो

कोई बात नहीं, मैं इसे अभी प्राप्त करता हूं। # आवृत्तियों के आधार पर ऑफसेट को स्केल करने की आवश्यकता है। अगर आपको हर बार 2 अक्षर खोने
पड़ रहे हैं

0
Regex.Replace(strInput, strToken.Replace("$", "[$]"), strReplaceWith, RegexOptions.IgnoreCase);

3
यह काम नहीं करता है। $ टोकन में नहीं है। यह स्ट्रिंग के साथ strReplace में है।
19

9
और आप इसे उसके लिए अनुकूलित नहीं कर सकते?
जोएल कोएहॉर्न

18
यह साइट सही उत्तरों के लिए एक भंडार माना जाता है। उत्तर नहीं जो लगभग सही हैं।
आहेओ

0

नियमित अभिव्यक्ति विधि काम करना चाहिए। हालाँकि आप जो भी कर सकते हैं वह डेटाबेस से स्ट्रिंग के मामले में कम होता है, आपके द्वारा किए गए% चर% के मामले में, और फिर डेटाबेस से निचले आवरण स्ट्रिंग में स्थिति और लंबाई का पता लगाता है। याद रखें, एक स्ट्रिंग में स्थितियां सिर्फ इसलिए नहीं बदल जाती हैं क्योंकि इसके निचले आवरण।

फिर एक लूप का उपयोग करना जो रिवर्स में जाता है (यह आसान है, यदि आप नहीं करते हैं, तो आपको एक चल रही गिनती को रखना होगा जहां बाद में अंक चलते हैं) डेटाबेस से अपने गैर-निचले आवरण वाले स्ट्रिंग से हटा दें%% चर उनकी स्थिति और% द्वारा लंबाई और प्रतिस्थापन मान डालें।


उल्टा, मेरा मतलब है कि पाया स्थानों को रिवर्स में सबसे छोटी से छोटी जगह पर संसाधित करें, न कि स्ट्रिंग को डेटाबेस से रिवर्स में फैलाएं।
cfeduke

आप, या आप रेगेक्स :)
रे

0

(चूंकि हर कोई इस पर एक शॉट ले रहा है)। यहाँ मेरा संस्करण है (अशक्त जांच, और सही इनपुट और प्रतिस्थापन से बचकर) ** इंटरनेट और अन्य संस्करणों के आसपास से प्रेरित:

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");

0

मुझे अपना मामला बनाने दो और फिर तुम चाहो तो मुझे किनारे तक फाड़ सकते हो।

रेगेक्स इस समस्या का जवाब नहीं है - बहुत धीमी और स्मृति भूख, अपेक्षाकृत बोल।

स्ट्रिंग मैनबलिंग की तुलना में स्ट्रिंगबर्ल बहुत बेहतर है।

चूंकि यह पूरक करने के लिए एक विस्तार विधि होगी string.Replace, इसलिए मुझे विश्वास है कि यह मैच के लिए महत्वपूर्ण है कि कैसे काम करता है - इसलिए एक ही तर्क के मुद्दों के लिए अपवाद फेंकना महत्वपूर्ण है क्योंकि मूल स्ट्रिंग वापस आ रही है यदि प्रतिस्थापन नहीं किया गया था।

मेरा मानना ​​है कि StringComparison पैरामीटर होना एक अच्छा विचार नहीं है। मैंने इसे आजमाया लेकिन मूल रूप से माइकल-ली द्वारा उल्लिखित परीक्षण मामले में एक समस्या दिखाई दी: -

[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();
    }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.