Thay thế nhiều ký tự trong chuỗi C #


178

Có cách nào tốt hơn để thay thế chuỗi?

Tôi ngạc nhiên rằng Thay thế không có trong một mảng ký tự hoặc mảng chuỗi. Tôi đoán rằng tôi có thể viết phần mở rộng của riêng mình nhưng tôi tò mò liệu có cách nào tốt hơn để làm như sau không? Lưu ý Thay thế cuối cùng là một chuỗi không phải là một ký tự.

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

Câu trả lời:


206

Bạn có thể sử dụng một biểu thức thay thế thông thường.

s/[;,\t\r ]|[\n]{2}/\n/g
  • s/ lúc bắt đầu có nghĩa là tìm kiếm
  • Các ký tự giữa []là các ký tự để tìm kiếm (theo bất kỳ thứ tự nào)
  • Thứ hai /phân định tìm kiếm - cho văn bản và văn bản thay thế

Trong tiếng Anh, nó đọc:

"Tìm kiếm ;hoặc ,hoặc \thoặc \rhoặc (không gian) hoặc chính xác hai tuần tự \nvà thay thế bằng \n"

Trong C #, bạn có thể làm như sau: (sau khi nhập System.Text.RegularExpressions)

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

2
\t\rđược bao gồm trong \s. Vì vậy, regex của bạn tương đương với [;,\s].
NullUserException

3
\sthực sự tương đương với việc [ \f\n\r\t\v]bạn đang đưa vào một số nội dung không có trong câu hỏi ban đầu. Ngoài ra, câu hỏi ban đầu yêu cầu Replace("\n\n", "\n")regex của bạn không xử lý.
NullUserException

11
Vui lòng xem xét rằng đối với các thao tác thay thế đơn giản mà người dùng không thể định cấu hình, sử dụng biểu thức thông thường sẽ không tối ưu vì nó rất chậm so với các thao tác chuỗi thông thường, theo bài viết điểm chuẩn đầu tiên tôi tìm thấy khi tìm kiếm "c # regex Performance thay thế" khoảng 13 thời gian chậm hơn.
quá

Ah regex, chữ tượng hình của sức mạnh! Vấn đề duy nhất tôi có thể thấy ở đây là khả năng đọc của con người trong các biểu thức chính quy; nhiều người từ chối hiểu chúng Gần đây tôi đã thêm một giải pháp dưới đây cho những người tìm kiếm một giải pháp thay thế ít phức tạp hơn.
sɐunıɔ qɐp

Vậy làm thế nào để chúng ta viết nếu chúng ta muốn thay thế nhiều ký tự bằng nhiều ký tự?
Habip Oğuz

114

Nếu bạn cảm thấy đặc biệt thông minh và không muốn sử dụng 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);

Bạn có thể gói nó trong một phương thức mở rộng với ít nỗ lực là tốt.

Chỉnh sửa: Hoặc chỉ cần đợi 2 phút và cuối cùng tôi sẽ viết nó :)

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

Và Voila...

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

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

Rất kém hiệu quả bộ nhớ, đặc biệt là cho các chuỗi lớn hơn.
MarcinJuraszek

@MarcinJuraszek Lol ... Đó có lẽ là lần đầu tiên tôi nghe thấy ai đó cho rằng các phương thức chuỗi tích hợp ít hiệu quả bộ nhớ hơn các biểu thức thông thường.
Paul Walls

10
Bạn đúng. Tôi nên đo trước khi tôi đăng nó. Tôi chạy điểm chuẩn và Regex.Replacechậm hơn 8 lần so với nhiều string.Replacecuộc gọi liên tiếp. và gấp 4 lần so với Split+ Join. Xem gist.github.com/MarcinJuraszek/c1437d925548561ba210a1c6ed144452
MarcinJuraszek

1
Giải pháp tốt đẹp! chỉ là một addon nhỏ. Thật không may, điều này sẽ không hoạt động nếu bạn muốn thay thế (các) ký tự đầu tiên. Giả sử bạn muốn thay thế ký tự 't' trong chuỗi ví dụ. Phương thức Split sẽ bỏ đi 't' của từ đầu tiên 'this' bởi vì nó là EmptyEntry. Nếu bạn sử dụng StringSplitOptions.None thay vì RemoveEmptyEntries, Split sẽ rời khỏi mục nhập và phương thức Tham gia sẽ thêm ký tự dấu phân cách thay thế. Hy vọng điều này sẽ giúp
Pierre

58

Bạn có thể sử dụng chức năng Tổng hợp của 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'));

Đây là phương pháp mở rộng:

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

Ví dụ sử dụng phương pháp mở rộng:

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

21

Đây là con đường ngắn nhất:

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

1
Điều này cũng giúp khi bạn cần điều này trong khởi tạo.
Guney Ozsan

8

Ohhh, hiệu suất kinh dị! Câu trả lời hơi lỗi thời, nhưng vẫn ...

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

Chuỗi chỉ là mảng char bất biến

Bạn chỉ cần làm cho nó có thể thay đổi:

  • hoặc bằng cách sử dụng StringBuilder
  • đi trong unsafethế giới và chơi với con trỏ (mặc dù nguy hiểm)

và cố gắng lặp lại qua các mảng ký tự với số lần ít nhất. Lưu ý HashSetở đây, vì nó tránh đi qua chuỗi ký tự bên trong vòng lặp. Nếu bạn cần tra cứu nhanh hơn nữa, bạn có thể thay thế HashSetbằng tra cứu tối ưu hóa char(dựa trên một array[256]).

Ví dụ với 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;
        }
    }
}

Chỉnh sửa - Phiên bản được tối ưu hóa

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

Sau đó, bạn chỉ cần sử dụng nó như thế này:

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

Hãy nhớ rằng Chuỗi nằm wchar_ttrong .net, bạn chỉ thay thế một tập hợp con của tất cả các ký tự có thể (và bạn sẽ cần 65536 bool để tối ưu hóa điều đó ...)
gog

3

Bạn cũng có thể chỉ cần viết các phương thức mở rộng chuỗi này và đặt chúng ở đâu đó trong giải pháp của bạn:

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


Gọi họ như thế này:

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

xyBxyDxy


Và điều này:

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

xyCxyF


2

Sử dụng RegEx.Replace, một cái gì đó như thế này:

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

Dưới đây là thông tin thêm về tài liệu MSDN này cho RegEx.Replace


1

Hiệu suất-Wise này có thể không phải là giải pháp tốt nhất nhưng nó hoạt động.

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(), "");
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.