Cách hiệu quả nhất để loại bỏ các ký tự đặc biệt khỏi chuỗi


266

Tôi muốn xóa tất cả các ký tự đặc biệt khỏi một chuỗi. Các ký tự được phép là AZ (chữ hoa hoặc chữ thường), số (0-9), dấu gạch dưới (_) hoặc dấu chấm (.).

Tôi có những điều sau đây, nó hoạt động nhưng tôi nghi ngờ (tôi biết!) Nó không hiệu quả lắm:

    public static string RemoveSpecialCharacters(string str)
    {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < str.Length; i++)
        {
            if ((str[i] >= '0' && str[i] <= '9')
                || (str[i] >= 'A' && str[i] <= 'z'
                    || (str[i] == '.' || str[i] == '_')))
                {
                    sb.Append(str[i]);
                }
        }

        return sb.ToString();
    }

Cách hiệu quả nhất để làm điều này là gì? Biểu thức chính quy sẽ trông như thế nào và so sánh với thao tác chuỗi thông thường như thế nào?

Các chuỗi sẽ được làm sạch sẽ khá ngắn, thường có độ dài từ 10 đến 30 ký tự.


5
Tôi sẽ không đưa ra câu trả lời vì nó sẽ không hiệu quả hơn, nhưng có một số phương thức char tĩnh như char.IsLetterOrDigit () mà bạn có thể sử dụng trong câu lệnh if của mình để làm cho nó dễ đọc hơn.
Martin Harris

5
Tôi không chắc chắn rằng việc kiểm tra từ A đến z là an toàn, trong đó nó mang đến 6 ký tự không theo thứ tự chữ cái, chỉ có một trong số đó là mong muốn (thanh dưới).
Steven Sudit

4
Tập trung vào làm cho mã của bạn dễ đọc hơn. trừ khi bạn đang làm điều này trong một vòng lặp như 500 lần một giây, hiệu quả không phải là vấn đề lớn. Sử dụng một biểu thức chính quy và nó sẽ dễ đọc hơn nhiều
Byron Whitlock

4
Byron, có lẽ bạn đúng về việc cần nhấn mạnh khả năng đọc. Tuy nhiên, tôi nghi ngờ về việc regrec có thể đọc được. :-)
Steven Sudit

2
Các biểu thức thông thường có thể đọc được hoặc không giống như tiếng Đức có thể đọc được hay không; nó phụ thuộc vào việc bạn có biết hay không (mặc dù trong cả hai trường hợp, bạn sẽ gặp các quy tắc ngữ pháp không có ý nghĩa;)
Blixt

Câu trả lời:


325

Tại sao bạn nghĩ rằng phương pháp của bạn không hiệu quả? Đó thực sự là một trong những cách hiệu quả nhất mà bạn có thể làm.

Tất nhiên bạn nên đọc ký tự thành một biến cục bộ hoặc sử dụng một liệt kê để giảm số lượng truy cập mảng:

public static string RemoveSpecialCharacters(this string str) {
   StringBuilder sb = new StringBuilder();
   foreach (char c in str) {
      if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '.' || c == '_') {
         sb.Append(c);
      }
   }
   return sb.ToString();
}

Một điều làm cho một phương pháp như thế này hiệu quả là nó có quy mô tốt. Thời gian thực hiện sẽ liên quan đến độ dài của chuỗi. Không có bất ngờ khó chịu nếu bạn sẽ sử dụng nó trên một chuỗi lớn.

Biên tập:
Tôi đã thực hiện một bài kiểm tra hiệu suất nhanh, chạy mỗi chức năng một triệu lần với chuỗi 24 ký tự. Đây là kết quả:

Chức năng ban đầu: 54,5 ms.
Thay đổi đề xuất của tôi: 47,1 ms.
Của tôi với cài đặt công suất StringBuilder: 43,3 ms.
Biểu thức chính quy: 294,4 ms.

Chỉnh sửa 2: Tôi đã thêm sự phân biệt giữa AZ và az trong mã ở trên. (Tôi chạy lại bài kiểm tra hiệu suất, và không có sự khác biệt đáng chú ý nào.)

Chỉnh sửa 3:
Tôi đã thử nghiệm giải pháp tra cứu + char [] và nó chạy trong khoảng 13 ms.

Tất nhiên, cái giá phải trả là việc khởi tạo bảng tra cứu khổng lồ và giữ nó trong bộ nhớ. Chà, đó không phải là nhiều dữ liệu, nhưng nó rất nhiều cho một chức năng tầm thường như vậy ...

private static bool[] _lookup;

static Program() {
   _lookup = new bool[65536];
   for (char c = '0'; c <= '9'; c++) _lookup[c] = true;
   for (char c = 'A'; c <= 'Z'; c++) _lookup[c] = true;
   for (char c = 'a'; c <= 'z'; c++) _lookup[c] = true;
   _lookup['.'] = true;
   _lookup['_'] = true;
}

public static string RemoveSpecialCharacters(string str) {
   char[] buffer = new char[str.Length];
   int index = 0;
   foreach (char c in str) {
      if (_lookup[c]) {
         buffer[index] = c;
         index++;
      }
   }
   return new string(buffer, 0, index);
}

4
Tôi đồng ý. Thay đổi khác duy nhất tôi sẽ thực hiện là thêm đối số dung lượng ban đầu vào hàm tạo StringBuilder, "= new StringBuilder (str.Lạng)".
David

2
Câu trả lời của tôi, sử dụng char[]bộ đệm chứ không phải StringBuilder, có một lợi thế nhỏ trên cái này theo thử nghiệm của tôi. (Mặc dù tôi ít đọc hơn, vì vậy lợi ích hiệu suất nhỏ có lẽ không đáng.)
LukeH

1
@Steven: Đó có thể là trường hợp, nhưng điểm chuẩn nói lên chính họ! Trong các thử nghiệm của tôi, sử dụng char[]bộ đệm thực hiện (hơi) tốt hơn StringBuilder, ngay cả khi nhân rộng lên chuỗi có độ dài hàng chục nghìn ký tự.
LukeH

10
@downvoter: Tại sao downvote? Nếu bạn không giải thích những gì bạn nghĩ là sai, nó không thể cải thiện câu trả lời.
Guffa

2
@SILENT: Không, không, nhưng bạn chỉ nên làm điều đó một lần. Nếu bạn phân bổ một mảng lớn mỗi lần bạn gọi phương thức (và nếu bạn gọi phương thức đó thường xuyên) thì phương thức sẽ trở nên chậm nhất và gây ra rất nhiều công việc cho trình thu gom rác.
Guffa

195

Chà, trừ khi bạn thực sự cần phải thực hiện chức năng của mình, chỉ cần đi với những gì dễ duy trì và hiểu nhất. Một biểu thức chính quy sẽ trông như thế này:

Để có hiệu suất bổ sung, bạn có thể biên dịch trước hoặc chỉ cần yêu cầu biên dịch trong cuộc gọi đầu tiên (các cuộc gọi tiếp theo sẽ nhanh hơn.)

public static string RemoveSpecialCharacters(string str)
{
    return Regex.Replace(str, "[^a-zA-Z0-9_.]+", "", RegexOptions.Compiled);
}

1
Tôi đoán rằng đây có lẽ là một truy vấn đủ phức tạp để nó nhanh hơn phương pháp của OP, đặc biệt là nếu được biên dịch trước. Tôi không có bằng chứng để sao lưu điều đó, tuy nhiên. Nó nên được thử nghiệm. Trừ khi nó chậm hơn nhiều, tôi sẽ chọn cách tiếp cận này bất kể, vì nó dễ đọc và bảo trì hơn. +1
rmeador

6
Đây là một regex rất đơn giản (không quay lại hoặc bất kỳ nội dung phức tạp nào trong đó) vì vậy nó sẽ khá nhanh.

9
@rmeador: nếu không được biên dịch thì nó chậm hơn khoảng 5x, biên dịch chậm hơn gấp 3 lần so với phương thức của anh ta. Vẫn đơn giản hơn gấp 10 lần :-D
user7116

6
Biểu thức thông thường không có búa ma thuật và không bao giờ nhanh hơn mã được tối ưu hóa bằng tay.
Christian Klauser

2
Đối với những người nhớ câu nói nổi tiếng của Knuth về tối ưu hóa, đây là nơi để bắt đầu. Sau đó, nếu bạn thấy rằng bạn cần thêm một phần nghìn của hiệu suất mili giây, hãy thực hiện với một trong những kỹ thuật khác.
John

15

Tôi đề nghị tạo một bảng tra cứu đơn giản mà bạn có thể khởi tạo trong hàm tạo tĩnh để đặt bất kỳ tổ hợp ký tự nào thành hợp lệ. Điều này cho phép bạn làm một kiểm tra nhanh chóng, duy nhất.

biên tập

Ngoài ra, về tốc độ, bạn sẽ muốn khởi tạo dung lượng của StringBuilder theo độ dài của chuỗi đầu vào. Điều này sẽ tránh sự phân bổ lại. Hai phương pháp này cùng nhau sẽ cung cấp cho bạn cả tốc độ và tính linh hoạt.

chỉnh sửa khác

Tôi nghĩ trình biên dịch có thể tối ưu hóa nó ra, nhưng như một vấn đề về phong cách cũng như hiệu quả, tôi khuyên bạn nên dùng foreach thay vì for.


Đối với mảng, forforeachsản xuất mã tương tự. Tôi không biết về chuỗi mặc dù. Tôi nghi ngờ rằng JIT biết về bản chất giống như mảng của String.
Christian Klauser

1
Tôi cá là JIT biết nhiều hơn về tính chất giống như chuỗi của chuỗi hơn là [trò đùa bị loại bỏ] của bạn. Anders etal đã thực hiện rất nhiều công việc tối ưu hóa mọi thứ về chuỗi trong .net

Tôi đã thực hiện điều này bằng cách sử dụng Hashset <char> và nó chậm hơn khoảng 2 lần so với phương pháp của anh ấy. Sử dụng bool [] hầu như không nhanh hơn (0,0469ms / iter v. 0,0559ms / iter) so với phiên bản anh ta có trong OP ... với vấn đề là ít đọc hơn.
dùng7116

1
Tôi không thể thấy bất kỳ sự khác biệt hiệu suất giữa việc sử dụng một mảng bool và một mảng int. Tôi sẽ sử dụng một mảng bool, vì nó đưa xuống bảng tra cứu từ 256 kb đến 64 kb, nhưng nó vẫn còn rất nhiều dữ liệu cho một hàm tầm thường như vậy ... Và nó chỉ nhanh hơn khoảng 30%.
Guffa

1
@Guffa 2) Vì chúng tôi chỉ giữ chữ số và một vài ký tự Latin cơ bản, chúng tôi chỉ cần một bảng cho byte thấp, vì vậy kích thước không thực sự là vấn đề. Nếu chúng ta muốn có mục đích chung, thì kỹ thuật Unicode tiêu chuẩn là hai hướng. Nói cách khác, một bảng gồm 256 bảng tham chiếu, nhiều trong số đó trỏ đến cùng một bảng trống.
Steven Sudit

12
public static string RemoveSpecialCharacters(string str)
{
    char[] buffer = new char[str.Length];
    int idx = 0;

    foreach (char c in str)
    {
        if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z')
            || (c >= 'a' && c <= 'z') || (c == '.') || (c == '_'))
        {
            buffer[idx] = c;
            idx++;
        }
    }

    return new string(buffer, 0, idx);
}

1
+1, đã thử nghiệm và nó nhanh hơn khoảng 40% so với StringBuilder. 0,0294ms / chuỗi v. 0,0399ms / chuỗi
user7116

Để chắc chắn, bạn có nghĩa là StringBuilder có hoặc không có phân bổ trước?
Steven Sudit

Với phân bổ trước, nó vẫn chậm hơn 40% so với phân bổ char [] và chuỗi mới.
dùng7116

2
Tôi thích điều này. Tôi đã điều chỉnh phương pháp nàyforeach (char c in input.Where(c => char.IsLetterOrDigit(c) || allowedSpecialCharacters.Any(x => x == c))) buffer[idx++] = c;
Chris Marisic

11

Một biểu thức chính quy sẽ giống như:

public string RemoveSpecialChars(string input)
{
    return Regex.Replace(input, @"[^0-9a-zA-Z\._]", string.Empty);
}

Nhưng nếu hiệu suất là rất quan trọng, tôi khuyên bạn nên thực hiện một số điểm chuẩn trước khi chọn "đường dẫn regex" ...


11

Nếu bạn đang sử dụng danh sách các ký tự động, LINQ có thể cung cấp giải pháp nhanh hơn và duyên dáng hơn nhiều:

public static string RemoveSpecialCharacters(string value, char[] specialCharacters)
{
    return new String(value.Except(specialCharacters).ToArray());
}

Tôi đã so sánh cách tiếp cận này với hai trong số các cách tiếp cận "nhanh" trước đó (biên dịch phát hành):

  • Giải pháp mảng Char của LukeH - 427 ms
  • Giải pháp StringBuilder - 429 ms
  • LINQ (câu trả lời này) - 98 ms

Lưu ý rằng thuật toán được sửa đổi một chút - các ký tự được truyền vào dưới dạng một mảng chứ không phải mã hóa cứng, điều này có thể ảnh hưởng đến mọi thứ một chút (tức là / các giải pháp khác sẽ có một vòng lặp bên trong để kiểm tra mảng ký tự).

Nếu tôi chuyển sang một giải pháp được mã hóa cứng bằng mệnh đề LINQ, thì kết quả là:

  • Giải pháp mảng Char - 7ms
  • Giải pháp StringBuilder - 22ms
  • LINQ - 60 ms

Có thể đáng để xem xét LINQ hoặc một cách tiếp cận được sửa đổi nếu bạn dự định viết một giải pháp chung chung hơn là mã hóa danh sách các ký tự. LINQ chắc chắn cung cấp cho bạn mã ngắn gọn, dễ đọc - thậm chí còn hơn cả Regex.


3
Cách tiếp cận này có vẻ tốt, nhưng nó không hoạt động - Ngoại trừ () là một thao tác được thiết lập, do đó bạn sẽ chỉ xuất hiện lần đầu tiên của mỗi ký tự duy nhất trong chuỗi.
McKenzieG1

5

Tôi không tin rằng thuật toán của bạn là bất cứ điều gì nhưng hiệu quả. Đó là O (n) và chỉ nhìn vào mỗi nhân vật một lần. Bạn sẽ không thể làm tốt hơn thế trừ khi bạn biết các giá trị một cách kỳ diệu trước khi kiểm tra chúng.

Tuy nhiên, tôi sẽ khởi tạo công suất của bạn StringBuildertheo kích thước ban đầu của chuỗi. Tôi đoán vấn đề hiệu suất nhận thức của bạn đến từ việc phân bổ lại bộ nhớ.

Lưu ý bên: Kiểm tra A- zkhông an toàn. Bạn đang bao gồm [, \, ], ^, _, và `...

Lưu ý bên 2: Để có thêm chút hiệu quả, hãy đặt các phép so sánh để giảm thiểu số lượng so sánh. (Tệ nhất, bạn đang nói 8 so sánh tho, vì vậy đừng suy nghĩ quá nhiều.) Điều này thay đổi với đầu vào dự kiến ​​của bạn, nhưng một ví dụ có thể là:

if (str[i] >= '0' && str[i] <= 'z' && 
    (str[i] >= 'a' || str[i] <= '9' ||  (str[i] >= 'A' && str[i] <= 'Z') || 
    str[i] == '_') || str[i] == '.')

Lưu ý bên 3: Nếu vì bất kỳ lý do gì bạn THỰC SỰ cần điều này nhanh, một câu lệnh chuyển đổi có thể nhanh hơn. Trình biên dịch sẽ tạo một bảng nhảy cho bạn, chỉ dẫn đến một so sánh duy nhất:

switch (str[i])
{
    case '0':
    case '1':
    .
    .
    .
    case '.':
        sb.Append(str[i]);
        break;
}

1
Tôi đồng ý rằng bạn không thể đánh bại O (n) về điều này. Tuy nhiên, có một chi phí cho mỗi so sánh có thể được hạ xuống. Tra cứu bảng có chi phí thấp, cố định, trong khi một loạt các so sánh sẽ tăng chi phí khi bạn thêm nhiều ngoại lệ.
Steven Sudit

Về lưu ý phụ 3, bạn có thực sự nghĩ rằng bảng nhảy sẽ nhanh hơn tra cứu bảng không?
Steven Sudit

Tôi đã chạy thử nghiệm hiệu năng nhanh trên giải pháp chuyển đổi và nó thực hiện giống như so sánh.
Guffa

@Steven Sudit - Tôi thực sự mạo hiểm giống nhau. Muốn chạy thử?
lc.

7
Ký hiệu O (n) đôi khi làm tôi bực mình. Mọi người sẽ đưa ra các giả định ngu ngốc dựa trên thực tế thuật toán đã là O (n). Nếu chúng tôi thay đổi thói quen này để thay thế các cuộc gọi str [i] bằng một hàm lấy giá trị so sánh bằng cách xây dựng kết nối SSL một lần với máy chủ ở phía đối diện ... bạn chắc chắn sẽ thấy hiệu suất lớn sự khác biệt và thuật toán là VẪN O (n). Chi phí O (1) cho mỗi thuật toán là đáng kể và KHÔNG tương đương!
Darron

4
StringBuilder sb = new StringBuilder();

for (int i = 0; i < fName.Length; i++)
{
   if (char.IsLetterOrDigit(fName[i]))
    {
       sb.Append(fName[i]);
    }
}

4

Bạn có thể sử dụng expresion thường xuyên như sau:

return Regex.Replace(strIn, @"[^\w\.@-]", "", RegexOptions.None, TimeSpan.FromSeconds(1.0));

3

Nó có vẻ tốt với tôi. Cải tiến duy nhất tôi sẽ làm là khởi tạo StringBuilderđộ dài của chuỗi.

StringBuilder sb = new StringBuilder(str.Length);

3

Tôi đồng ý với mẫu mã này. Chỉ khác là tôi biến nó thành Phương thức mở rộng của kiểu chuỗi. Vì vậy, bạn có thể sử dụng nó trong một dòng hoặc mã rất đơn giản:

string test = "abc@#$123";
test.RemoveSpecialCharacters();

Cảm ơn Guffa cho thí nghiệm của bạn.

public static class MethodExtensionHelper
    {
    public static string RemoveSpecialCharacters(this string str)
        {
            StringBuilder sb = new StringBuilder();
            foreach (char c in str)
            {
                if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
                {
                    sb.Append(c);
                }
            }
            return sb.ToString();
        }
}

2

Tôi sẽ sử dụng Thay thế chuỗi bằng Biểu thức chính quy tìm kiếm "ký tự đặc biệt", thay thế tất cả các ký tự được tìm thấy bằng một chuỗi trống.


+1 chắc chắn ít mã hơn và có thể dễ đọc hơn bỏ qua Regex viết một lần.
kenny

1
@kenny - Tôi đồng ý. Câu hỏi ban đầu thậm chí nói rằng các chuỗi ngắn - 10-30 ký tự. Nhưng rõ ràng nhiều người vẫn nghĩ rằng chúng tôi đang bán thời gian CPU vào lần thứ hai ...
Tom Bushell

Reguler expressin hoạt động rất lười biếng. Vì vậy, nó không nên được sử dụng luôn.
RockOnGom

2

Tôi đã phải làm một cái gì đó tương tự cho công việc, nhưng trong trường hợp của tôi, tôi phải lọc tất cả những thứ không phải là một chữ cái, số hoặc khoảng trắng (nhưng bạn có thể dễ dàng sửa đổi nó theo nhu cầu của bạn). Việc lọc được thực hiện ở phía máy khách trong JavaScript, nhưng vì lý do bảo mật, tôi cũng đang thực hiện việc lọc phía máy chủ. Vì tôi có thể mong đợi hầu hết các chuỗi được sạch sẽ, tôi muốn tránh sao chép chuỗi trừ khi tôi thực sự cần. Điều này cho phép tôi thực hiện dưới đây, sẽ thực hiện tốt hơn cho cả chuỗi sạch và bẩn.

public static string EnsureOnlyLetterDigitOrWhiteSpace(string input)
{
    StringBuilder cleanedInput = null;
    for (var i = 0; i < input.Length; ++i)
    {
        var currentChar = input[i];
        var charIsValid = char.IsLetterOrDigit(currentChar) || char.IsWhiteSpace(currentChar);

        if (charIsValid)
        {
            if(cleanedInput != null)
                cleanedInput.Append(currentChar);
        }
        else
        {
            if (cleanedInput != null) continue;
            cleanedInput = new StringBuilder();
            if (i > 0)
                cleanedInput.Append(input.Substring(0, i));
        }
    }

    return cleanedInput == null ? input : cleanedInput.ToString();
}

1

Đối với S & G's, Linq-ified cách:

var original = "(*^%foo)(@)&^@#><>?:\":';=-+_";
var valid = new char[] { 
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 
    'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 
    'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 
    'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', 
    '9', '0', '.', '_' };
var result = string.Join("",
    (from x in original.ToCharArray() 
     where valid.Contains(x) select x.ToString())
        .ToArray());

Tuy nhiên, tôi không nghĩ rằng đây sẽ là cách hiệu quả nhất.


2
Không, bởi vì đó là một tìm kiếm tuyến tính.
Steven Sudit

1
public string RemoveSpecial(string evalstr)
{
StringBuilder finalstr = new StringBuilder();
            foreach(char c in evalstr){
            int charassci = Convert.ToInt16(c);
            if (!(charassci >= 33 && charassci <= 47))// special char ???
             finalstr.append(c);
            }
return finalstr.ToString();
}

1

Sử dụng:

s.erase(std::remove_if(s.begin(), s.end(), my_predicate), s.end());

bool my_predicate(char c)
{
 return !(isalpha(c) || c=='_' || c==' '); // depending on you definition of special characters
}

Và bạn sẽ có được một chuỗi sạch s.

erase()sẽ loại bỏ tất cả các ký tự đặc biệt và có khả năng tùy biến cao với my_predicate()chức năng.


1

Hashset là O (1)
Không chắc nó có nhanh hơn so với so sánh hiện có không

private static HashSet<char> ValidChars = new HashSet<char>() { 'a', 'b', 'c', 'A', 'B', 'C', '1', '2', '3', '_' };
public static string RemoveSpecialCharacters(string str)
{
    StringBuilder sb = new StringBuilder(str.Length / 2);
    foreach (char c in str)
    {
        if (ValidChars.Contains(c)) sb.Append(c);
    }
    return sb.ToString();
}

Tôi đã thử nghiệm và điều này không nhanh hơn câu trả lời được chấp nhận.
Tôi sẽ để nó lên như thể bạn cần một bộ ký tự có thể cấu hình được, đây sẽ là một giải pháp tốt.


Tại sao bạn nghĩ rằng so sánh không phải là O (1)?
Guffa

@Guffa Tôi không chắc là không và tôi đã xóa bình luận của mình. Và +1. Tôi nên đã thực hiện nhiều thử nghiệm trước khi đưa ra nhận xét.
paparazzo

1

Tôi tự hỏi nếu một sự thay thế dựa trên Regex (có thể được biên dịch) là nhanh hơn. Sẽ phải kiểm tra rằng Ai đó đã tìm thấy điều này chậm hơn ~ 5 lần.

Ngoài ra, bạn nên khởi tạo StringBuilder với độ dài dự kiến, để chuỗi trung gian không phải sao chép xung quanh trong khi nó phát triển.

Một số tốt là độ dài của chuỗi gốc hoặc một cái gì đó thấp hơn một chút (tùy thuộc vào bản chất của các hàm đầu vào).

Cuối cùng, bạn có thể sử dụng bảng tra cứu (trong phạm vi 0..127) để tìm hiểu xem một ký tự có được chấp nhận hay không.


Một biểu thức chính quy đã được kiểm tra và nó chậm hơn khoảng năm lần. Với bảng tra cứu trong phạm vi 0..127, bạn vẫn phải kiểm tra phạm vi mã ký tự trước khi sử dụng bảng tra cứu, vì các ký tự là giá trị 16 bit, không phải giá trị 7 bit.
Guffa

@Guffa Err ... có? ;)
Christian Klauser

1

Đoạn mã sau có đầu ra sau (kết luận là chúng ta cũng có thể lưu một số tài nguyên bộ nhớ phân bổ kích thước mảng nhỏ hơn):

lookup = new bool[123];

for (var c = '0'; c <= '9'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

for (var c = 'A'; c <= 'Z'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

for (var c = 'a'; c <= 'z'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

48: 0  
49: 1  
50: 2  
51: 3  
52: 4  
53: 5  
54: 6  
55: 7  
56: 8  
57: 9  
65: A  
66: B  
67: C  
68: D  
69: E  
70: F  
71: G  
72: H  
73: I  
74: J  
75: K  
76: L  
77: M  
78: N  
79: O  
80: P  
81: Q  
82: R  
83: S  
84: T  
85: U  
86: V  
87: W  
88: X  
89: Y  
90: Z  
97: a  
98: b  
99: c  
100: d  
101: e  
102: f  
103: g  
104: h  
105: i  
106: j  
107: k  
108: l  
109: m  
110: n  
111: o  
112: p  
113: q  
114: r  
115: s  
116: t  
117: u  
118: v  
119: w  
120: x  
121: y  
122: z  

Bạn cũng có thể thêm các dòng mã sau để hỗ trợ ngôn ngữ Nga (kích thước mảng sẽ là 1104):

for (var c = 'А'; c <= 'Я'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

for (var c = 'а'; c <= 'я'; c++)
{
    lookup[c] = true; System.Diagnostics.Debug.WriteLine((int)c + ": " + (char)c);
}

1

Tôi không chắc đó là cách hiệu quả nhất, nhưng nó hiệu quả với tôi

 Public Function RemoverTildes(stIn As String) As String
    Dim stFormD As String = stIn.Normalize(NormalizationForm.FormD)
    Dim sb As New StringBuilder()

    For ich As Integer = 0 To stFormD.Length - 1
        Dim uc As UnicodeCategory = CharUnicodeInfo.GetUnicodeCategory(stFormD(ich))
        If uc <> UnicodeCategory.NonSpacingMark Then
            sb.Append(stFormD(ich))
        End If
    Next
    Return (sb.ToString().Normalize(NormalizationForm.FormC))
End Function

Câu trả lời không hoạt động, nhưng câu hỏi dành cho C #. . (Một điều, converter.telerik.com )
Momoro

1

Có rất nhiều giải pháp được đề xuất ở đây, một số hiệu quả hơn những giải pháp khác, nhưng có lẽ không dễ đọc lắm. Đây là một thứ có thể không hiệu quả nhất, nhưng chắc chắn có thể sử dụng được trong hầu hết các tình huống, và khá ngắn gọn và dễ đọc, tận dụng Linq:

string stringToclean = "This is a test.  Do not try this at home; you might get hurt. Don't believe it?";

var validPunctuation = new HashSet<char>(". -");

var cleanedVersion = new String(stringToclean.Where(x => (x >= 'A' && x <= 'Z') || (x >= 'a' && x <= 'z') || validPunctuation.Contains(x)).ToArray());

var cleanedLowercaseVersion = new String(stringToclean.ToLower().Where(x => (x >= 'a' && x <= 'z') || validPunctuation.Contains(x)).ToArray());

-1
public static string RemoveSpecialCharacters(string str){
    return str.replaceAll("[^A-Za-z0-9_\\\\.]", "");
}

1
Tôi sợ replaceAllkhông phải là hàm Chuỗi C # mà là Java hoặc JavaScript
Csaba Toth

-1
public static string RemoveAllSpecialCharacters(this string text) {
  if (string.IsNullOrEmpty(text))
    return text;

  string result = Regex.Replace(text, "[:!@#$%^&*()}{|\":?><\\[\\]\\;'/.,~]", " ");
  return result;
}

Trả lời là sai. Nếu bạn sẽ sử dụng regex, nó nên được bao gồm, không bao gồm độc quyền, vì bạn bỏ lỡ một số ký tự. Trên thực tế, đã có câu trả lời với regex. Và để được đầy đủ - regex là SLOWER sau đó so sánh trực tiếp chức năng ký tự.
TPAKTOPA

-3

Nếu bạn lo lắng về tốc độ, hãy sử dụng các con trỏ để chỉnh sửa chuỗi hiện có. Bạn có thể ghim chuỗi và lấy một con trỏ tới nó, sau đó chạy một vòng lặp for trên mỗi ký tự, ghi đè lên mỗi ký tự không hợp lệ bằng một ký tự thay thế. Nó sẽ cực kỳ hiệu quả và không yêu cầu phân bổ bất kỳ bộ nhớ chuỗi mới nào. Bạn cũng sẽ cần biên dịch mô-đun của mình với tùy chọn không an toàn và thêm công cụ sửa đổi "không an toàn" vào tiêu đề phương thức của bạn để sử dụng các con trỏ.

static void Main(string[] args)
{
    string str = "string!$%with^&*invalid!!characters";
    Console.WriteLine( str ); //print original string
    FixMyString( str, ' ' );
    Console.WriteLine( str ); //print string again to verify that it has been modified
    Console.ReadLine(); //pause to leave command prompt open
}


public static unsafe void FixMyString( string str, char replacement_char )
{
    fixed (char* p_str = str)
    {
        char* c = p_str; //temp pointer, since p_str is read-only
        for (int i = 0; i < str.Length; i++, c++) //loop through each character in string, advancing the character pointer as well
            if (!IsValidChar(*c)) //check whether the current character is invalid
                (*c) = replacement_char; //overwrite character in existing string with replacement character
    }
}

public static bool IsValidChar( char c )
{
    return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c == '.' || c == '_');
    //return char.IsLetterOrDigit( c ) || c == '.' || c == '_'; //this may work as well
}

14
Không! Thay đổi một chuỗi trong .NET là BAAAAAAAAAAAAD! Mọi thứ trong khung đều dựa trên quy tắc rằng các chuỗi là bất biến, và nếu bạn phá vỡ điều đó, bạn có thể nhận được các tác dụng phụ rất đáng ngạc nhiên ...
Guffa
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.