แทนที่อักขระหลายตัวในสตริง C #


178

มีวิธีที่ดีกว่าในการแทนที่สตริงหรือไม่

ฉันประหลาดใจที่การแทนที่ไม่ได้ใช้อาร์เรย์อักขระหรืออาร์เรย์สตริง ฉันเดาว่าฉันสามารถเขียนส่วนขยายของตัวเอง แต่ฉันอยากรู้ว่ามีวิธีที่ดีกว่าในการทำสิ่งต่อไปนี้หรือไม่? แจ้งให้ทราบการแทนที่ล่าสุดคือสตริงไม่ใช่อักขระ

myString.Replace(';', '\n').Replace(',', '\n').Replace('\r', '\n').Replace('\t', '\n').Replace(' ', '\n').Replace("\n\n", "\n");

คำตอบ:


206

คุณสามารถใช้แทนที่นิพจน์ปกติ

s/[;,\t\r ]|[\n]{2}/\n/g
  • s/ ที่จุดเริ่มต้นหมายถึงการค้นหา
  • อักขระระหว่าง[และ]เป็นอักขระที่จะค้นหา (ในลำดับใดก็ได้)
  • วินาที/คั่นข้อความค้นหาและข้อความแทนที่

ในภาษาอังกฤษสิ่งนี้อ่าน:

"ค้นหา;หรือ,หรือ\tหรือ\rหรือ(ช่องว่าง) หรือลำดับที่สองอย่างแน่นอน\nและแทนที่ด้วย\n"

ใน C # คุณสามารถทำสิ่งต่อไปนี้: (หลังจากนำเข้าSystem.Text.RegularExpressions)

Regex pattern = new Regex("[;,\t\r ]|[\n]{2}");
pattern.Replace(myString, "\n");

2
\tและจะรวมอยู่ใน\r \sดังนั้น regex [;,\s]ของคุณจะเทียบเท่ากับ
NullUserException

3
และ \sเทียบเท่ากับ[ \f\n\r\t\v]ดังนั้นคุณจึงรวมสิ่งต่าง ๆ ที่นั่นซึ่งไม่ได้อยู่ในคำถามเดิม นอกจากนี้คำถามเดิมจะถามว่าReplace("\n\n", "\n")regex ของคุณไม่รองรับ
NullUserException

11
โปรดพิจารณาว่าสำหรับการดำเนินการแทนที่อย่างง่ายซึ่งผู้ใช้ไม่สามารถกำหนดค่าได้การใช้นิพจน์ทั่วไปนั้นไม่เหมาะสมเนื่องจากมันช้ามากเมื่อเทียบกับการดำเนินการสตริงปกติตามบทความมาตรฐานแรกที่ฉันพบเมื่อค้นหา "c # regex ประสิทธิภาพแทนที่" ประมาณ 13 เวลาช้าลง
เกินไป

อา regex พลังแห่งพลัง! ปัญหาเดียวที่ฉันเห็นที่นี่คือความสามารถในการอ่านของมนุษย์ในการแสดงออกปกติ หลายคนปฏิเสธที่จะเข้าใจพวกเขา ฉันเพิ่งเพิ่มโซลูชันด้านล่างสำหรับผู้ที่มองหาทางเลือกที่ซับซ้อนน้อยลง
sɐunıɔןɐqɐp

แล้วเราจะเขียนอย่างไรถ้าเราต้องการแทนที่อักขระหลายตัวด้วยอักขระหลายตัว?
Habip Oğuz

114

หากคุณรู้สึกฉลาดเป็นพิเศษและไม่ต้องการใช้ Regex:

char[] separators = new char[]{' ',';',',','\r','\t','\n'};

string s = "this;is,\ra\t\n\n\ntest";
string[] temp = s.Split(separators, StringSplitOptions.RemoveEmptyEntries);
s = String.Join("\n", temp);

คุณสามารถห่อสิ่งนี้ด้วยวิธีการต่อขยายได้โดยใช้ความพยายามเพียงเล็กน้อยเช่นกัน

แก้ไข: หรือรอ 2 นาทีแล้วฉันจะเขียนมันต่อไป :)

public static class ExtensionMethods
{
   public static string Replace(this string s, char[] separators, string newVal)
   {
       string[] temp;

       temp = s.Split(separators, StringSplitOptions.RemoveEmptyEntries);
       return String.Join( newVal, temp );
   }
}

และ voila ...

char[] separators = new char[]{' ',';',',','\r','\t','\n'};
string s = "this;is,\ra\t\n\n\ntest";

s = s.Replace(separators, "\n");

หน่วยความจำไม่มีประสิทธิภาพโดยเฉพาะอย่างยิ่งสำหรับสตริงขนาดใหญ่
MarcinJuraszek

@MarcinJuraszek ฮ่า ๆ ... นั่นอาจเป็นครั้งแรกที่ฉันเคยได้ยินว่ามีคนอ้างว่าเมธอดสตริงในตัวมีประสิทธิภาพหน่วยความจำน้อยกว่านิพจน์ทั่วไป
Paul Walls

10
คุณถูก. ฉันควรวัดได้ก่อนโพสต์นั้น ฉันรันเกณฑ์มาตรฐานและRegex.Replaceช้ากว่า 8x มากกว่าการstring.Replaceโทรหลายครั้งติดต่อกัน และ 4x ช้ากว่า+Split Joinดูgist.github.com/MarcinJuraszek/c1437d925548561ba210a1c6ed144452
MarcinJuraszek

1
ทางออกที่ดี! เพียงแค่ addon เล็ก ๆ น่าเสียดายที่วิธีนี้ใช้ไม่ได้หากคุณต้องการให้อักขระตัวแรกถูกแทนที่เช่นกัน สมมติว่าคุณต้องการแทนที่อักขระ 't' ในสตริงตัวอย่าง วิธีการแยกจะเพียงแค่วางที่ 't' ของคำแรก 'นี้' เพราะมันเป็น EmptyEntry ถ้าคุณใช้การ StringSplitOptions.None แทน RemoveEmptyEntries การแยกจะออกจากรายการและวิธีการเข้าร่วมจะเพิ่มอักขระตัวแยกแทน หวังว่านี่จะช่วยได้
Pierre

58

คุณสามารถใช้ฟังก์ชันการรวมของ Linq:

string s = "the\nquick\tbrown\rdog,jumped;over the lazy fox.";
char[] chars = new char[] { ' ', ';', ',', '\r', '\t', '\n' };
string snew = chars.Aggregate(s, (c1, c2) => c1.Replace(c2, '\n'));

นี่คือวิธีการขยาย:

public static string ReplaceAll(this string seed, char[] chars, char replacementCharacter)
{
    return chars.Aggregate(seed, (str, cItem) => str.Replace(cItem, replacementCharacter));
}

ตัวอย่างการใช้วิธีการขยาย:

string snew = s.ReplaceAll(chars, '\n');

21

นี่คือวิธีที่สั้นที่สุด:

myString = Regex.Replace(myString, @"[;,\t\r ]|[\n]{2}", "\n");

1
ซับหนึ่งนี้ยังช่วยเมื่อคุณต้องการใน initializers
Guney Ozsan

8

โอ้วววละครเรื่องสยองขวัญ! คำตอบนั้นล้าสมัยไปแล้ว แต่ยัง ...

public static class StringUtils
{
    #region Private members

    [ThreadStatic]
    private static StringBuilder m_ReplaceSB;

    private static StringBuilder GetReplaceSB(int capacity)
    {
        var result = m_ReplaceSB;

        if (null == result)
        {
            result = new StringBuilder(capacity);
            m_ReplaceSB = result;
        }
        else
        {
            result.Clear();
            result.EnsureCapacity(capacity);
        }

        return result;
    }


    public static string ReplaceAny(this string s, char replaceWith, params char[] chars)
    {
        if (null == chars)
            return s;

        if (null == s)
            return null;

        StringBuilder sb = null;

        for (int i = 0, count = s.Length; i < count; i++)
        {
            var temp = s[i];
            var replace = false;

            for (int j = 0, cc = chars.Length; j < cc; j++)
                if (temp == chars[j])
                {
                    if (null == sb)
                    {
                        sb = GetReplaceSB(count);
                        if (i > 0)
                            sb.Append(s, 0, i);
                    }

                    replace = true;
                    break;
                }

            if (replace)
                sb.Append(replaceWith);
            else
                if (null != sb)
                    sb.Append(temp);
        }

        return null == sb ? s : sb.ToString();
    }
}

7

สตริงเป็นอาร์เรย์ถ่านที่ไม่เปลี่ยนรูป

คุณเพียงแค่ต้องทำให้มันไม่แน่นอน:

  • ทั้งโดยใช้ StringBuilder
  • ไปในunsafeโลกและเล่นกับพอยน์เตอร์(อันตรายแม้ว่า)

และพยายามวนซ้ำอักขระจำนวนน้อยที่สุด โปรดสังเกตที่HashSetนี่เนื่องจากมันจะหลีกเลี่ยงการเรียงลำดับอักขระภายในลูป หากคุณต้องการการค้นหาที่เร็วยิ่งขึ้นคุณสามารถแทนที่HashSetด้วยการค้นหาที่ปรับให้เหมาะสมสำหรับchar(อิงตามarray[256])

ตัวอย่างด้วย StringBuilder

public static void MultiReplace(this StringBuilder builder, 
    char[] toReplace, 
    char replacement)
{
    HashSet<char> set = new HashSet<char>(toReplace);
    for (int i = 0; i < builder.Length; ++i)
    {
        var currentCharacter = builder[i];
        if (set.Contains(currentCharacter))
        {
            builder[i] = replacement;
        }
    }
}

แก้ไข - รุ่นที่ปรับให้เหมาะสม

public static void MultiReplace(this StringBuilder builder, 
    char[] toReplace,
    char replacement)
{
    var set = new bool[256];
    foreach (var charToReplace in toReplace)
    {
        set[charToReplace] = true;
    }
    for (int i = 0; i < builder.Length; ++i)
    {
        var currentCharacter = builder[i];
        if (set[currentCharacter])
        {
            builder[i] = replacement;
        }
    }
}

จากนั้นคุณก็ใช้สิ่งนี้:

var builder = new StringBuilder("my bad,url&slugs");
builder.MultiReplace(new []{' ', '&', ','}, '-');
var result = builder.ToString();

โปรดจำไว้ว่า Strings นั้นอยู่wchar_tใน. net คุณกำลังแทนที่เพียงเซตย่อยของอักขระที่เป็นไปได้ทั้งหมด (และคุณจะต้องมี 65536 bools เพื่อปรับให้เหมาะสม ... )
gog

3

นอกจากนี้คุณยังสามารถเขียนวิธีการต่อสายอักขระเหล่านี้และวางไว้ในโซลูชันของคุณ:

using System.Text;

public static class StringExtensions
{
    public static string ReplaceAll(this string original, string toBeReplaced, string newValue)
    {
        if (string.IsNullOrEmpty(original) || string.IsNullOrEmpty(toBeReplaced)) return original;
        if (newValue == null) newValue = string.Empty;
        StringBuilder sb = new StringBuilder();
        foreach (char ch in original)
        {
            if (toBeReplaced.IndexOf(ch) < 0) sb.Append(ch);
            else sb.Append(newValue);
        }
        return sb.ToString();
    }

    public static string ReplaceAll(this string original, string[] toBeReplaced, string newValue)
    {
        if (string.IsNullOrEmpty(original) || toBeReplaced == null || toBeReplaced.Length <= 0) return original;
        if (newValue == null) newValue = string.Empty;
        foreach (string str in toBeReplaced)
            if (!string.IsNullOrEmpty(str))
                original = original.Replace(str, newValue);
        return original;
    }
}


เรียกพวกเขาเช่นนี้

"ABCDE".ReplaceAll("ACE", "xy");

xyBxyDxy


และนี่:

"ABCDEF".ReplaceAll(new string[] { "AB", "DE", "EF" }, "xy");

xyCxyF


2

ใช้ RegEx เปลี่ยนตำแหน่งดังนี้:

  string input = "This is   text with   far  too   much   " + 
                 "whitespace.";
  string pattern = "[;,]";
  string replacement = "\n";
  Regex rgx = new Regex(pattern);
  string result = rgx.Replace(input, replacement);

นี่คือข้อมูลเพิ่มเติมเกี่ยวกับเอกสาร MSDNนี้สำหรับ RegEx


1

ประสิทธิภาพที่ชาญฉลาดอาจเป็นวิธีที่ดีที่สุด แต่ใช้งานได้ดี

var str = "filename:with&bad$separators.txt";
char[] charArray = new char[] { '#', '%', '&', '{', '}', '\\', '<', '>', '*', '?', '/', ' ', '$', '!', '\'', '"', ':', '@' };
foreach (var singleChar in charArray)
{
   str = str.Replace(singleChar, '_');
}

1
string ToBeReplaceCharacters = @"~()@#$%&amp;+,'&quot;&lt;&gt;|;\/*?";
string fileName = "filename;with<bad:separators?";

foreach (var RepChar in ToBeReplaceCharacters)
{
    fileName = fileName.Replace(RepChar.ToString(), "");
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.