Xóa ký tự khỏi chuỗi C #


150

Làm thế nào tôi có thể loại bỏ các ký tự từ một chuỗi? Ví dụ : "My name @is ,Wan.;'; Wan".

Tôi muốn xóa các ký tự '@', ',', '.', ';', '\''khỏi chuỗi đó để nó trở thành"My name is Wan Wan"

Câu trả lời:


177
var str = "My name @is ,Wan.;'; Wan";
var charsToRemove = new string[] { "@", ",", ".", ";", "'" };
foreach (var c in charsToRemove)
{
    str = str.Replace(c, string.Empty);
}

Nhưng tôi có thể đề xuất một cách tiếp cận khác nếu bạn muốn xóa tất cả các ký tự không phải chữ cái

var str = "My name @is ,Wan.;'; Wan";
str = new string((from c in str
                  where char.IsWhiteSpace(c) || char.IsLetterOrDigit(c)
                  select c
       ).ToArray());

12
Cũng có thể được thực hiện như thế này, str = new string (str.Where (x => char.IsWhiteSpace (x) || char.IsLetterOrDigit (x)). ToArray ());
Adnan Bhatti

1
Tôi đã phải tìm kiếm điều này, chuỗi .Empty không tạo ra một chuỗi để so sánh để nó hiệu quả hơn "". ( stackoverflow.com/questions/151472/ khăn )
Tom Cerul

6
Tôi có phải là người duy nhất nhận được "Đối số 2: không thể chuyển đổi từ 'chuỗi' thành 'char'" om chuỗi.Empty?
OddDev

2
@OddDev bạn chỉ nên gặp lỗi này nếu mảng mà bạn lặp qua là danh sách các ký tự. Nếu chúng là chuỗi thì nó sẽ hoạt động
Nhà phát triển Newteq

3
Ngoài ra, xin lưu ý rằng để hàm "str.Replace" hoạt động chính xác, tham số đầu tiên phải là "chuỗi" nếu bạn muốn sử dụng chuỗi.Empty làm tham số thứ hai. Nếu bạn sử dụng một char (tức là 'a') làm tham số đầu tiên, bạn cũng sẽ cần một char làm tham số thứ hai. Nếu không, bạn sẽ nhận được lỗi "Đối số 2: không thể chuyển đổi từ 'chuỗi' sang 'char'" được đề cập bởi @OddDev ở trên
Leo


64

Âm thanh giống như một ứng dụng lý tưởng cho RegEx - một công cụ được thiết kế để thao tác văn bản nhanh. Trong trường hợp này:

Regex.Replace("He\"ll,o Wo'r.ld", "[@,\\.\";'\\\\]", string.Empty)

3
Có vẻ như điều này sẽ hiệu quả hơn nhiều so với cách tiếp cận dựa trên iterator đặc biệt là nếu bạn có thể sử dụng Regex được biên dịch;
Ade Miller

Đây phải là câu trả lời được chấp nhận, đặc biệt bởi vì, như @AdeMiller đã nói, nó sẽ hiệu quả hơn nhiều.
Obsidian

14
Đây không phải là nhanh hơn vòng lặp, đó là một quan niệm sai lầm phổ biến rằng regex luôn luôn nhanh hơn các vòng lặp. Regex không phải là ma thuật, tại một thời điểm nào đó, họ phải lặp lại chuỗi để thực hiện các hoạt động của mình và họ có thể chậm hơn nhiều với các chi phí từ chính regex. Chúng thực sự nổi trội khi có các thao tác cực kỳ phức tạp, trong đó cần hàng chục dòng mã và nhiều vòng lặp. Thử nghiệm phiên bản đã biên dịch của regex này với một vòng lặp không được tối ưu hóa đơn giản 50000 lần, regex chậm hơn 6 lần.
Tony Cheetham

Hiệu quả bộ nhớ thì sao? Các biểu thức chính quy có hiệu quả hơn trong việc phân bổ các chuỗi mới không?
Marek

2
Có lẽ tôi đã sai chính tả khi tôi khẳng định rằng RegEx rất nhanh. Trừ khi điều này là trung tâm của một vòng lặp rất chặt chẽ sau đó là những cân nhắc khác, khả năng đọc và bảo trì như vậy có khả năng chi phối hiệu suất cho một hoạt động nhỏ như thế này.
John Melville

21

Ít cụ thể hơn cho câu hỏi của bạn, có thể xóa TẤT CẢ dấu chấm câu khỏi một chuỗi (ngoại trừ khoảng trắng) bằng cách liệt kê màu trắng các ký tự được chấp nhận trong một biểu thức thông thường:

string dirty = "My name @is ,Wan.;'; Wan";

// only space, capital A-Z, lowercase a-z, and digits 0-9 are allowed in the string
string clean = Regex.Replace(dirty, "[^A-Za-z0-9 ]", "");

Lưu ý có một khoảng trắng sau 9 đó để không xóa khoảng trắng khỏi câu của bạn. Đối số thứ ba là một chuỗi rỗng dùng để thay thế bất kỳ chuỗi con nào không thuộc biểu thức chính quy.


19

So sánh các đề xuất khác nhau (cũng như so sánh trong bối cảnh thay thế một ký tự với các kích cỡ và vị trí khác nhau của mục tiêu).

Trong trường hợp cụ thể này, việc chia nhỏ các mục tiêu và tham gia vào các thay thế (trong trường hợp này, chuỗi trống) là nhanh nhất ít nhất là một hệ số 3. Cuối cùng, hiệu suất sẽ khác nhau tùy thuộc vào số lần thay thế, trong đó các thay thế nằm trong nguồn và kích thước của nguồn. #ymmv

Các kết quả

(kết quả đầy đủ ở đây )

| Test                      | Compare | Elapsed                                                            |
|---------------------------|---------|--------------------------------------------------------------------|
| SplitJoin                 | 1.00x   | 29023 ticks elapsed (2.9023 ms) [in 10K reps, 0.00029023 ms per]   |
| Replace                   | 2.77x   | 80295 ticks elapsed (8.0295 ms) [in 10K reps, 0.00080295 ms per]   |
| RegexCompiled             | 5.27x   | 152869 ticks elapsed (15.2869 ms) [in 10K reps, 0.00152869 ms per] |
| LinqSplit                 | 5.43x   | 157580 ticks elapsed (15.758 ms) [in 10K reps, 0.0015758 ms per]   |
| Regex, Uncompiled         | 5.85x   | 169667 ticks elapsed (16.9667 ms) [in 10K reps, 0.00169667 ms per] |
| Regex                     | 6.81x   | 197551 ticks elapsed (19.7551 ms) [in 10K reps, 0.00197551 ms per] |
| RegexCompiled Insensitive | 7.33x   | 212789 ticks elapsed (21.2789 ms) [in 10K reps, 0.00212789 ms per] |
| Regex Insentive           | 7.52x   | 218164 ticks elapsed (21.8164 ms) [in 10K reps, 0.00218164 ms per] |

Kiểm tra khai thác (LinqPad)

(lưu ý: PerfVsđang mở rộng thời gian tôi đã viết )

void test(string title, string sample, string target, string replacement) {
    var targets = target.ToCharArray();

    var tox = "[" + target + "]";
    var x = new Regex(tox);
    var xc = new Regex(tox, RegexOptions.Compiled);
    var xci = new Regex(tox, RegexOptions.Compiled | RegexOptions.IgnoreCase);

    // no, don't dump the results
    var p = new Perf/*<string>*/();
        p.Add(string.Join(" ", title, "Replace"), n => targets.Aggregate(sample, (res, curr) => res.Replace(new string(curr, 1), replacement)));
        p.Add(string.Join(" ", title, "SplitJoin"), n => String.Join(replacement, sample.Split(targets)));
        p.Add(string.Join(" ", title, "LinqSplit"), n => String.Concat(sample.Select(c => targets.Contains(c) ? replacement : new string(c, 1))));
        p.Add(string.Join(" ", title, "Regex"), n => Regex.Replace(sample, tox, replacement));
        p.Add(string.Join(" ", title, "Regex Insentive"), n => Regex.Replace(sample, tox, replacement, RegexOptions.IgnoreCase));
        p.Add(string.Join(" ", title, "Regex, Uncompiled"), n => x.Replace(sample, replacement));
        p.Add(string.Join(" ", title, "RegexCompiled"), n => xc.Replace(sample, replacement));
        p.Add(string.Join(" ", title, "RegexCompiled Insensitive"), n => xci.Replace(sample, replacement));

    var trunc = 40;
    var header = sample.Length > trunc ? sample.Substring(0, trunc) + "..." : sample;

    p.Vs(header);
}

void Main()
{
    // also see /programming/7411438/remove-characters-from-c-sharp-string

    "Control".Perf(n => { var s = "*"; });


    var text = "My name @is ,Wan.;'; Wan";
    var clean = new[] { '@', ',', '.', ';', '\'' };

    test("stackoverflow", text, string.Concat(clean), string.Empty);


    var target = "o";
    var f = "x";
    var replacement = "1";

    var fillers = new Dictionary<string, string> {
        { "short", new String(f[0], 10) },
        { "med", new String(f[0], 300) },
        { "long", new String(f[0], 1000) },
        { "huge", new String(f[0], 10000) }
    };

    var formats = new Dictionary<string, string> {
        { "start", "{0}{1}{1}" },
        { "middle", "{1}{0}{1}" },
        { "end", "{1}{1}{0}" }
    };

    foreach(var filler in fillers)
    foreach(var format in formats) {
        var title = string.Join("-", filler.Key, format.Key);
        var sample = string.Format(format.Value, target, filler.Value);

        test(title, sample, target, replacement);
    }
}

1
Cuối cùng là một số! Làm tốt lắm @drzaus!
Marek



6

Một giải pháp đơn giản khác:

var forbiddenChars = @"@,.;'".ToCharArray();
var dirty = "My name @is ,Wan.;'; Wan";
var clean = new string(dirty.Where(c => !forbiddenChars.Contains(c)).ToArray());

5
new List<string> { "@", ",", ".", ";", "'" }.ForEach(m => str = str.Replace(m, ""));

4

Chuỗi chỉ là một mảng ký tự, vì vậy hãy sử dụng Linq để thực hiện thay thế (tương tự như Albin ở trên ngoại trừ sử dụng câu lệnh chứa linq để thực hiện thay thế):

var resultString = new string(
        (from ch in "My name @is ,Wan.;'; Wan"
         where ! @"@,.;\'".Contains(ch)
         select ch).ToArray());

Chuỗi đầu tiên là chuỗi để thay thế các ký tự và chuỗi thứ hai là một chuỗi đơn giản chứa các ký tự


Giải pháp Linq của Albin có lẽ tốt hơn, trừ khi có thêm ký tự bạn muốn lọc ra (không được bao phủ bởi khoảng trắng và chữ và chữ số).
alistair

3

Tôi cũng có thể ném nó ra đây.

Tạo một phần mở rộng để xóa ký tự khỏi chuỗi:

public static string RemoveChars(this string input, params char[] chars)
{
    var sb = new StringBuilder();
    for (int i = 0; i < input.Length; i++)
    {
        if (!chars.Contains(input[i]))
            sb.Append(input[i]);
    }
    return sb.ToString();
}

Và nó có thể sử dụng như thế này:

string str = "My name @is ,Wan.;'; Wan";
string cleanedUpString = str.RemoveChars('@', ',', '.', ';', '\'');

Hoặc chỉ như thế này:

string str = "My name @is ,Wan.;'; Wan".RemoveChars('@', ',', '.', ';', '\'');

Đây là giải pháp tốt nhất, vì nó tạo ra số lượng phân bổ bộ nhớ nhỏ nhất. Tôi cũng sẽ đặt độ dài của chuỗi gốc là công suất ban đầu của trình tạo chuỗi, như: StringBuilder mới (input.Ldrops) cho mục đích này có số lượng cấp phát bộ nhớ ít nhất.
thủ quỹ

3

Dường như cách ngắn nhất là kết hợp LINQ và string.Concat:

var input = @"My name @is ,Wan.;'; Wan";
var chrs = new[] {'@', ',', '.', ';', '\''};
var result = string.Concat(input.Where(c => !chrs.Contains(c)));
// => result = "My name is Wan Wan" 

Xem bản demo C # . Lưu ý rằng đó string.Concatlà một phím tắt để string.Join("", ...).

Lưu ý rằng sử dụng regex để loại bỏ các ký tự riêng lẻ vẫn có thể xây dựng linh hoạt, mặc dù người ta tin rằng regex chậm hơn. Tuy nhiên, đây là một cách để xây dựng một regex động như vậy (trong đó tất cả những gì bạn cần là một lớp nhân vật):

var pattern = $"[{Regex.Escape(new string(chrs))}]+";
var result = Regex.Replace(input, pattern, string.Empty);

Xem bản demo C # khác . Regex sẽ trông giống như [@,\.;']+(một khớp trở lên ( +) xuất hiện liên tiếp @, ,, ., ;hoặc 'ký tự), nơi dấu chấm không phải trốn thoát, nhưng Regex.Escapesẽ là cần thiết để thoát chars khác mà phải được thoát ra, như \, ^, ]hoặc -có vị trí bên trong lớp nhân vật bạn không thể dự đoán.



3

Đây là một phương pháp tôi đã viết có một cách tiếp cận hơi khác. Thay vì chỉ định các ký tự cần xóa, tôi nói với phương thức của mình những ký tự tôi muốn giữ - nó sẽ xóa tất cả các ký tự khác.

Trong ví dụ của OP, anh ta chỉ muốn giữ các ký tự và khoảng trắng theo thứ tự chữ cái. Đây là một cuộc gọi đến phương thức của tôi sẽ như thế nào ( bản demo C # ):

var str = "My name @is ,Wan.;'; Wan";

// "My name is Wan Wan"
var result = RemoveExcept(str, alphas: true, spaces: true);

Đây là phương pháp của tôi:

/// <summary>
/// Returns a copy of the original string containing only the set of whitelisted characters.
/// </summary>
/// <param name="value">The string that will be copied and scrubbed.</param>
/// <param name="alphas">If true, all alphabetical characters (a-zA-Z) will be preserved; otherwise, they will be removed.</param>
/// <param name="numerics">If true, all alphabetical characters (a-zA-Z) will be preserved; otherwise, they will be removed.</param>
/// <param name="dashes">If true, all alphabetical characters (a-zA-Z) will be preserved; otherwise, they will be removed.</param>
/// <param name="underlines">If true, all alphabetical characters (a-zA-Z) will be preserved; otherwise, they will be removed.</param>
/// <param name="spaces">If true, all alphabetical characters (a-zA-Z) will be preserved; otherwise, they will be removed.</param>
/// <param name="periods">If true, all decimal characters (".") will be preserved; otherwise, they will be removed.</param>
public static string RemoveExcept(string value, bool alphas = false, bool numerics = false, bool dashes = false, bool underlines = false, bool spaces = false, bool periods = false) {
    if (string.IsNullOrWhiteSpace(value)) return value;
    if (new[] { alphas, numerics, dashes, underlines, spaces, periods }.All(x => x == false)) return value;

    var whitelistChars = new HashSet<char>(string.Concat(
        alphas ? "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" : "",
        numerics ? "0123456789" : "",
        dashes ? "-" : "",
        underlines ? "_" : "",
        periods ? "." : "",
        spaces ? " " : ""
    ).ToCharArray());

    var scrubbedValue = value.Aggregate(new StringBuilder(), (sb, @char) => {
        if (whitelistChars.Contains(@char)) sb.Append(@char);
        return sb;
    }).ToString();

    return scrubbedValue;
}

Câu trả lời tuyệt vời!
edtheprogrammerguy

Rất đẹp! chuỗi số có 0 hai lần.
John Kurtz

@JohnKurtz Bắt tốt đẹp - bây giờ đã biến mất.
Mass Dot Net

2

Có rất nhiều câu trả lời hay ở đây, đây là phần bổ sung của tôi cùng với một số bài kiểm tra đơn vị có thể được sử dụng để giúp kiểm tra tính chính xác, giải pháp của tôi tương tự như @ Rianne ở trên nhưng sử dụng ISet để cung cấp thời gian tra cứu O (1) cho các ký tự thay thế (và cả tương tự như giải pháp Linq của @Albin Sunnanbo).

    using System;
    using System.Collections.Generic;
    using System.Linq;

    /// <summary>
    /// Returns a string with the specified characters removed.
    /// </summary>
    /// <param name="source">The string to filter.</param>
    /// <param name="removeCharacters">The characters to remove.</param>
    /// <returns>A new <see cref="System.String"/> with the specified characters removed.</returns>
    public static string Remove(this string source, IEnumerable<char> removeCharacters)
    {
        if (source == null)
        {
            throw new  ArgumentNullException("source");
        }

        if (removeCharacters == null)
        {
            throw new ArgumentNullException("removeCharacters");
        }

        // First see if we were given a collection that supports ISet
        ISet<char> replaceChars = removeCharacters as ISet<char>;

        if (replaceChars == null)
        {
            replaceChars = new HashSet<char>(removeCharacters);
        }

        IEnumerable<char> filtered = source.Where(currentChar => !replaceChars.Contains(currentChar));

        return new string(filtered.ToArray());
    }

Các bài kiểm tra NUnit (2.6+) tại đây

using System;
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;

[TestFixture]
public class StringExtensionMethodsTests
{
    [TestCaseSource(typeof(StringExtensionMethodsTests_Remove_Tests))]
    public void Remove(string targetString, IEnumerable<char> removeCharacters, string expected)
    {
        string actual = StringExtensionMethods.Remove(targetString, removeCharacters);

        Assert.That(actual, Is.EqualTo(expected));
    }

    [TestCaseSource(typeof(StringExtensionMethodsTests_Remove_ParameterValidation_Tests))]
    public void Remove_ParameterValidation(string targetString, IEnumerable<char> removeCharacters)
    {
        Assert.Throws<ArgumentNullException>(() => StringExtensionMethods.Remove(targetString, removeCharacters));
    }
}

internal class StringExtensionMethodsTests_Remove_Tests : IEnumerable
{
    public IEnumerator GetEnumerator()
    {
        yield return new TestCaseData("My name @is ,Wan.;'; Wan", new char[] { '@', ',', '.', ';', '\'' }, "My name is Wan Wan").SetName("StringUsingCharArray");
        yield return new TestCaseData("My name @is ,Wan.;'; Wan", new HashSet<char> { '@', ',', '.', ';', '\'' }, "My name is Wan Wan").SetName("StringUsingISetCollection");
        yield return new TestCaseData(string.Empty, new char[1], string.Empty).SetName("EmptyStringNoReplacementCharactersYieldsEmptyString");
        yield return new TestCaseData(string.Empty, new char[] { 'A', 'B', 'C' }, string.Empty).SetName("EmptyStringReplacementCharsYieldsEmptyString");
        yield return new TestCaseData("No replacement characters", new char[1], "No replacement characters").SetName("StringNoReplacementCharactersYieldsString");
        yield return new TestCaseData("No characters will be replaced", new char[] { 'Z' }, "No characters will be replaced").SetName("StringNonExistantReplacementCharactersYieldsString");
        yield return new TestCaseData("AaBbCc", new char[] { 'a', 'C' }, "ABbc").SetName("CaseSensitivityReplacements");
        yield return new TestCaseData("ABC", new char[] { 'A', 'B', 'C' }, string.Empty).SetName("AllCharactersRemoved");
        yield return new TestCaseData("AABBBBBBCC", new char[] { 'A', 'B', 'C' }, string.Empty).SetName("AllCharactersRemovedMultiple");
        yield return new TestCaseData("Test That They Didn't Attempt To Use .Except() which returns distinct characters", new char[] { '(', ')' }, "Test That They Didn't Attempt To Use .Except which returns distinct characters").SetName("ValidateTheStringIsNotJustDistinctCharacters");
    }
}

internal class StringExtensionMethodsTests_Remove_ParameterValidation_Tests : IEnumerable
{
    public IEnumerator GetEnumerator()
    {
        yield return new TestCaseData(null, null);
        yield return new TestCaseData("valid string", null);
        yield return new TestCaseData(null, new char[1]);
    }
}

2

Đây là một phương pháp mạnh mẽ mà tôi thường sử dụng trong cùng một trường hợp:

private string Normalize(string text)
{
        return string.Join("",
            from ch in text
            where char.IsLetterOrDigit(ch) || char.IsWhiteSpace(ch)
            select ch);
}

Thưởng thức...


1

Trường cũ sao chép / stomp:

  private static string RemoveDirtyCharsFromString(string in_string)
     {
        int index = 0;
        int removed = 0;

        byte[] in_array = Encoding.UTF8.GetBytes(in_string);

        foreach (byte element in in_array)
        {
           if ((element == ' ') ||
               (element == '-') ||
               (element == ':'))
           {
              removed++;
           }
           else
           {
              in_array[index] = element;
              index++;
           }
        }

        Array.Resize<byte>(ref in_array, (in_array.Length - removed));
        return(System.Text.Encoding.UTF8.GetString(in_array, 0, in_array.Length));
     }

Không chắc chắn về hiệu quả của các phương thức khác (nghĩa là tổng phí của tất cả các lệnh gọi và khởi tạo hàm xảy ra như một hiệu ứng phụ trong thực thi C #).


1

Tôi làm cho nó phương thức mở rộng và với mảng chuỗi, tôi nghĩ string[]là hữu ích hơn char[]vì char cũng có thể là chuỗi:

public static class Helper
{
    public static string RemoverStrs(this string str, string[] removeStrs)
    {
        foreach (var removeStr in removeStrs)
            str = str.Replace(removeStr, "");
        return str;
    }
}

sau đó bạn có thể sử dụng nó ở bất cứ đâu:

string myname = "My name @is ,Wan.;'; Wan";
string result = myname.RemoveStrs(new[]{ "@", ",", ".", ";", "\\"});

1

Tôi cần xóa các ký tự đặc biệt khỏi tệp XML. Đây là cách tôi đã làm nó. char.ToString () là anh hùng trong mã này.

string item = "<item type="line" />"
char DC4 = (char)0x14;
string fixed = item.Replace(DC4.ToString(), string.Empty);

1
new[] { ',', '.', ';', '\'', '@' }
.Aggregate("My name @is ,Wan.;'; Wan", (s, c) => s.Replace(c.ToString(), string.Empty)); 

1

Lấy số liệu hiệu suất từ ​​@drzaus, đây là một phương pháp mở rộng sử dụng thuật toán nhanh nhất.

public static class StringEx
{
    public static string RemoveCharacters(this string s, params char[] unwantedCharacters) 
        => s == null ? null : string.Join(string.Empty, s.Split(unwantedCharacters));
}

Sử dụng

var name = "edward woodward!";
var removeDs = name.RemoveCharacters('d', '!');
Assert.Equal("ewar woowar", removeDs); // old joke
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.