سؤال

أحتاج إلى البحث في سلسلة واستبدال كافة تكرارات %FirstName% و %PolicyAmount% مع قيمة مأخوذة من قاعدة البيانات.المشكلة هي أن الكتابة بالأحرف الكبيرة للاسم الأول تختلف.وهذا يمنعني من استخدام String.Replace() طريقة.لقد رأيت صفحات الويب حول هذا الموضوع الذي يقترح

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

ولكن لسبب ما عندما أحاول واستبدال %PolicyAmount% مع $0, ، لا يحدث الاستبدال أبدًا.أفترض أن الأمر يتعلق بكون علامة الدولار حرفًا محجوزًا في التعبير العادي.

هل هناك طريقة أخرى يمكنني استخدامها لا تتضمن تعقيم المدخلات للتعامل مع أحرف regex الخاصة؟

هل كانت مفيدة؟

المحلول

من MSDN
$ 0 - "يستبدل مشاركة فرعية عن طريق رقم رقم المجموعة (عشري) يقابل"

في التعابير العادية. 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);
}

لذا...

  • هذا هو طريقة التمديد @ مارك روبنسون
  • هذا لا يحاول تخطي Regex @Helge (عليك حقًا القيام بالبايت بايت إذا كنت تريد شم السلسلة مثل هذا خارج Regex)
  • يمرر @MichaelLiu حالة اختبار ممتازة, "œ".ReplaceCaseInsensitiveFind("oe", ""), ، على الرغم من أنه ربما كان لديه سلوك مختلف قليلاً في ذهنه.

للأسف، تعليق @HA الذي يجب عليك القيام به Escape الثلاثة ليست صحيحة.القيمة الأولية و newValue لا يلزم أن يكون.

ملحوظة: ومع ذلك، عليك الهروب $s في القيمة الجديدة التي تقوم بإدراجها إذا كانوا جزءًا مما قد يبدو أنه علامة "قيمة تم التقاطها"..وبالتالي فإن علامات الدولار الثلاثة في Regex.Replace داخل Regex.Replace [كذا].وبدون ذلك، شيء من هذا القبيل ينكسر.

"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 على الترميزات) للتأكد تمامًا من حصولك على ما قصدته لحالات الاستخدام المهمة.يذكرني كروكفورد على "التعبيرات العادية غير الآمنة" القليل.في كثير من الأحيان، نكتب تعبيرات تعبيرية تسمح بما نريده (إذا كنا محظوظين)، ولكننا نسمح بالمزيد (على سبيل المثال، هل $10 حقًا سلسلة "قيمة التقاط" صالحة في regexp newValue الخاص بي، أعلاه؟) لأننا لم نكن مدروسين بما فيه الكفاية.كلتا الطريقتين لهما قيمة، وكلاهما يشجعان أنواعًا مختلفة من الأخطاء غير المقصودة.غالبًا ما يكون من السهل التقليل من أهمية التعقيد.

هذا غريب $ الهروب (وهذا Regex.Escape لم يفلت من أنماط القيمة التي تم التقاطها مثل $0 كما كنت أتوقع في قيم الاستبدال) دفعني إلى الجنون لفترة من الوقت.البرمجة صعبة (ج) 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;
    }

}

ويبدو أن أسهل طريقة هي ببساطة استخدام الأسلوب الذي يأتي مع صافي وكانت موجودة منذ صافي 1.0 استبدال:

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

ومن أجل استخدام هذه الطريقة، لديك لإضافة مرجع إلى Microsoft.VisualBasic assemblly. يعتبر هذا التجمع جزءا أساسيا من وقت تشغيل صافي، أنه ليس من حمل إضافي أو وضع علامة على أنه عفا عليها الزمن.

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

وبناء على الجواب جيف ريدي، مع بعض تحقيق أمثلية والتصديقات:

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. التنين، ولكن لأنه إذا ما عليك سوى بديل واحد:

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

ويجب أن تعمل طريقة التعبير العادية. ومع ذلك ما يمكنك القيام به أيضا هو الحالة الأدنى سلسلة من قاعدة البيانات، وانخفاض الحالة٪ المتغيرات٪ لديك، ومن ثم تحديد المواقف وأطوال في سلسلة أقل فتش من قاعدة البيانات. تذكر، ومواقف في سلسلة لا تتغير لمجرد السفلية فتش.

وبعد ذلك باستخدام حلقة أن يذهب في الاتجاه المعاكس (من الأسهل، إذا لم يكن سيكون لديك للحفاظ على العد قيد التشغيل من حيث تتحرك نقطة لاحقة) إزالة من غير أدنى سلسلة الخاص بك فتش من قاعدة البيانات٪ المتغيرات٪ من موقفهم وطول وإدراج القيم الاستبدال.

و(لأن الجميع يأخذ رصاصة واحدة في هذا). وهنا نسختي (مع شيكات فارغة، والإدخال الصحيح واستبدال الهروب) ** مستوحاة من جميع أنحاء شبكة الإنترنت، وإصدارات أخرى:

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

واسمحوا لي أن حالتي وبعد ذلك يمكنك المسيل للدموع لي الى اشلاء إذا أردت.

والتعبيرات المنتظمة ليست هي الحل لهذه المشكلة - بطيئة جدا والذاكرة الجياع، نسبيا

وب StringBuilder هو أفضل بكثير من تغيير اسم السلسلة.

وبما أن هذه ستكون طريقة تمديد لاستكمال string.Replace، وأعتقد أنه من المهم أن تتطابق كيفية عمل ذلك - لذا رمي استثناءات للقضايا الحجة نفسها المهم كما هو إعادة السلسلة الأصلية إذا لم تقدم بديلا

وأعتقد أن وجود المعلمة StringComparison ليست فكرة جيدة. لم تحاول ذلك، ولكن حالة اختبار من قبل مايكل ليو المذكورة أصلا أظهر مشكلة: -

[TestCase("œ", "oe", "", StringComparison.InvariantCultureIgnoreCase, Result = "")]

وبينما IndexOf سوف تطابق، هناك عدم تطابق بين طول المباراة في سلسلة المصدر (1) وoldValue.Length (2). وهذا يتجلى من خلال التسبب IndexOutOfRange في بعض الحلول الأخرى عندما أضيف oldValue.Length لموقف مباراة الحالي وأنا لا يمكن أن تجد طريقة للالتفاف حول ذلك. التعابير المنطقية فشل لتتناسب مع الحالة على أي حال، لذلك أخذت الحل العملي فقط باستخدام 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();
    }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top