स्ट्रिंग से विशेष वर्णों को निकालने का सबसे कुशल तरीका


266

मैं एक स्ट्रिंग से सभी विशेष पात्रों को निकालना चाहता हूं। अनुमत वर्ण AZ (अपरकेस या लोअरकेस), संख्याएँ (0-9), अंडरस्कोर (_), या डॉट संकेत (!) हैं।

मेरे पास निम्नलिखित हैं, यह काम करता है लेकिन मुझे संदेह है (मुझे पता है!) यह बहुत कुशल नहीं है:

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

ऐसा करने का सबसे कुशल तरीका क्या है? एक नियमित अभिव्यक्ति क्या दिखती है, और यह सामान्य स्ट्रिंग हेरफेर के साथ कैसे तुलना करती है?

जिन तारों को साफ किया जाएगा, वे कम होंगे, आमतौर पर लंबाई में 10 और 30 अक्षरों के बीच।


5
मैं इसे किसी उत्तर में नहीं डालूंगा क्योंकि यह कोई और अधिक कुशल नहीं होगा, लेकिन char.IsLetterOrDigit () जैसे कई स्थिर चार तरीके हैं, जिन्हें आप कम से कम अधिक उपयोगी बनाने के लिए अपने बयान में उपयोग कर सकते हैं।
मार्टिन हैरिस

5
मुझे यकीन नहीं है कि ए टू जेड के लिए जाँच करना सुरक्षित है, इसमें वह 6 वर्णों को लाता है जो वर्णमाला नहीं हैं, जिनमें से केवल एक वांछित है (अंडरबार)।
स्टीवन सुदित

4
अपने कोड को अधिक पठनीय बनाने पर ध्यान दें। जब तक आप इसे लूप में 500 बार सेकंड की तरह कर रहे हैं, दक्षता कोई बड़ी बात नहीं है। एक regexp का उपयोग करें और इसे पढ़ना बहुत आसान हो जाएगा।
बायरन

4
बायरन, आप शायद पठनीयता पर जोर देने के बारे में सही हैं। हालाँकि, मैं regexp के पठनीय होने पर संदेह कर रहा हूँ। :-)
स्टीवन सुदित

2
नियमित अभिव्यक्ति पठनीय हो या न हो, जर्मन की तरह पठनीय है या नहीं; यह इस बात पर निर्भर करता है कि आप इसे जानते हैं या नहीं (हालाँकि दोनों ही मामलों में आप हर बार और फिर व्याकरण के नियमों के अनुसार आएंगे, इसका कोई मतलब नहीं है;)
Blixt

जवाबों:


325

आपको क्यों लगता है कि आपकी विधि कुशल नहीं है? यह वास्तव में सबसे कुशल तरीकों में से एक है जिसे आप कर सकते हैं।

आपको निश्चित रूप से चरित्र को एक स्थानीय चर में पढ़ना चाहिए या सरणी पहुंच की संख्या को कम करने के लिए एक एन्यूमरेटर का उपयोग करना चाहिए:

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

एक चीज जो इस तरह से कुशल बनाती है, वह यह है कि यह अच्छी तरह से मापी जाती है। निष्पादन का समय स्ट्रिंग की लंबाई के सापेक्ष होगा। यदि आप इसे एक बड़े स्ट्रिंग पर उपयोग करेंगे तो कोई आश्चर्य की बात नहीं है।

संपादित करें:
मैंने एक त्वरित प्रदर्शन परीक्षण किया, प्रत्येक फ़ंक्शन को 24 वर्ण स्ट्रिंग के साथ एक लाख बार चलाया। ये परिणाम हैं:

मूल फ़ंक्शन: 54.5 एमएस।
मेरे सुझाए गए बदलाव: 47.1 एमएस।
StringBuilder क्षमता की स्थापना के साथ खान: 43.3 एमएस।
नियमित अभिव्यक्ति: 294.4 एमएस।

संपादन 2: मैंने उपरोक्त कोड में AZ और az के बीच अंतर जोड़ा। (मैं प्रदर्शन परीक्षण फिर से करता हूं, और कोई उल्लेखनीय अंतर नहीं है।)

संपादन 3:
मैंने लुकअप + चार [] समाधान का परीक्षण किया, और यह लगभग 13 एमएस में चलता है।

भुगतान करने की कीमत, ज़ाहिर है, विशाल लुकअप तालिका का आरंभ और इसे स्मृति में रखना है। खैर, यह इतना डेटा नहीं है, लेकिन इस तरह के एक तुच्छ फ़ंक्शन के लिए यह बहुत है ...

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
मैं सहमत हूँ। एकमात्र अन्य परिवर्तन जो मैं करूंगा, वह है प्रारंभिक क्षमता तर्क को StringBuilder कंस्ट्रक्टर में जोड़ना, "= नया StringBuilder (str.Length)"।
डेविड

2
मेरे जवाब, char[]बजाय एक बफर का उपयोग कर StringBuilder, मेरे परीक्षण के अनुसार इस पर एक मामूली बढ़त है। (हालांकि मेरा कम पठनीय है, इसलिए छोटे प्रदर्शन का लाभ शायद इसके लायक नहीं है।)
ल्यूक

1
@ उत्तर: यह अच्छी तरह से मामला हो सकता है, लेकिन बेंचमार्क खुद के लिए बोलते हैं! मेरे परीक्षणों में, char[]बफर का उपयोग करने से (थोड़ा) बेहतर होता है StringBuilder, तब भी जब लंबाई में हजारों वर्णों वाले तार को स्केल किया जाता है।
ल्यूक जूल

10
@downvoter: डाउनवोट क्यों? यदि आप यह नहीं समझाते कि आप क्या सोचते हैं, तो यह उत्तर को बेहतर नहीं कर सकता।
गुफ़ा

2
@ प्रतिनिधि: नहीं, यह नहीं है, लेकिन आपको केवल एक बार ऐसा करना चाहिए। यदि आप किसी ऐसी सरणी को आवंटित करते हैं जो हर बार बड़े पैमाने पर आपको कॉल करती है (और यदि आप बार-बार विधि को कॉल करते हैं), तो विधि दूर तक सबसे धीमी हो जाती है, और कचरा संग्रहकर्ता के लिए बहुत काम का कारण बनती है।
गुफ़ा

195

ठीक है, जब तक आपको वास्तव में अपने फ़ंक्शन से प्रदर्शन को निचोड़ने की ज़रूरत नहीं है, बस बनाए रखें जो कि बनाए रखना और समझना सबसे आसान है। एक नियमित अभिव्यक्ति इस तरह दिखाई देगी:

अतिरिक्त प्रदर्शन के लिए, आप या तो इसे पूर्व-संकलित कर सकते हैं या इसे पहले कॉल पर संकलित करने के लिए कह सकते हैं (बाद में कॉल तेज़ हो जाएगा।)

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

1
मुझे लगता है कि यह शायद एक जटिल पर्याप्त प्रश्न है कि यह ओपी के दृष्टिकोण से तेज होगा, खासकर अगर पूर्व-संकलित। मेरे पास हालांकि इस बात का कोई सबूत नहीं है। इसका परीक्षण होना चाहिए। जब तक यह बहुत धीमा नहीं होता, मैं इस दृष्टिकोण की परवाह किए बिना चुनता हूं, क्योंकि यह पढ़ने और बनाए रखने के लिए आसान है। +1
rmeador

6
इसका एक बहुत ही सरल रेक्सक्स (इसमें कोई बैकट्रैकिंग या कोई जटिल सामान नहीं है) इसलिए यह बहुत तेज़ होना चाहिए।

9
@rmeador: इसे संकलित किए बिना यह लगभग 5x धीमा है, संकलित यह उसकी विधि से 3x धीमा है। अभी भी 10x सरल हालांकि :-D
user7116

6
नियमित अभिव्यक्ति कोई जादुई हथौड़े नहीं हैं और हाथ से अनुकूलित कोड की तुलना में कभी तेज नहीं होते हैं।
क्रिश्चियन क्लाऊजर

2
अनुकूलन के बारे में नूथ के प्रसिद्ध उद्धरण को याद करने वालों के लिए, यह वह जगह है जहां से शुरू करना है। फिर, यदि आप पाते हैं कि आपको एक मिलीसेकंड प्रदर्शन के अतिरिक्त हजारवें हिस्से की आवश्यकता है, तो अन्य तकनीकों में से एक के साथ जाएं।
जॉन

15

मेरा सुझाव है कि एक साधारण लुकअप टेबल बनाई जाए, जिसे आप स्थिर कंस्ट्रक्टर में वर्णों के किसी भी संयोजन को मान्य करने के लिए आरंभ कर सकते हैं। यह आपको एक त्वरित, एकल जांच करने देता है।

संपादित करें

इसके अलावा, गति के लिए, आप अपने स्ट्रिंग स्ट्रिंग की लंबाई को अपने स्ट्रिंगब्यूलर की क्षमता को इनिशियलाइज़ करना चाहेंगे। इससे वसूली से बचना होगा। ये दोनों तरीके एक साथ आपको गति और लचीलापन दोनों देंगे।

एक और संपादन

मुझे लगता है कि कंपाइलर इसे ऑप्टिमाइज़ कर सकता है, लेकिन स्टाइल के साथ-साथ दक्षता के मामले में, मैं इसके बजाय फॉरचेक की सलाह देता हूं।


सरणियों के लिए, forऔर foreachसमान कोड का उत्पादन करें। मैं तार के बारे में नहीं जानता। मुझे संदेह है कि JIT को स्ट्रिंग की तरह की प्रकृति के बारे में पता है।
क्रिश्चियन क्लॉसर

1
मैं शर्त लगाता हूं कि JIT आपके [जोक हटाए गए] की तुलना में स्ट्रिंग की तरह की प्रकृति के बारे में अधिक जानता है। एंडर्स एटल ने स्ट्रिंग्स के बारे में सब कुछ अनुकूलन करने में बहुत काम किया। नेट

मैंने इसे HashSet <char> का उपयोग करके किया है और यह अपने तरीके से 2x धीमी है। बूल [] का उपयोग करना बमुश्किल तेज़ होता है (0.0469ms / iter v। 0.0559ms / iter) वह संस्करण जो ओपी में है ... की तुलना में कम पठनीय होने की समस्या के साथ।
user7116

1
मैं बूल सरणी और इंट सरणी का उपयोग करने के बीच कोई प्रदर्शन अंतर नहीं देख सका। मैं एक बूल सरणी का उपयोग करूंगा, क्योंकि यह लुकअप टेबल को 256 केबी से 64 केबी तक नीचे लाता है, लेकिन यह अभी भी इस तरह के एक तुच्छ फ़ंक्शन के लिए बहुत अधिक डेटा है ... और यह केवल लगभग 30% तेज है।
गुफा जूल

1
@Guffa 2) यह देखते हुए कि हम केवल अल्फ़ान्यूमेरिक्स और कुछ बुनियादी लैटिन वर्णों को रख रहे हैं, हमें केवल कम बाइट के लिए एक तालिका की आवश्यकता है, इसलिए आकार वास्तव में कोई समस्या नहीं है। यदि हम सामान्य उद्देश्य चाहते थे, तो मानक यूनिकोड तकनीक दोहरा-अप्रत्यक्ष है। दूसरे शब्दों में, 256 तालिका संदर्भों की एक तालिका, जिनमें से कई एक ही खाली तालिका की ओर इशारा करती हैं।
स्टीवन सुदित

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, परीक्षण किया गया और यह StringBuilder की तुलना में लगभग 40% तेज है। 0.0294ms / स्ट्रिंग वी। 0.0399ms / स्ट्रिंग
user7116

बस यह सुनिश्चित करने के लिए, क्या आपका मतलब है स्ट्रिंगरब्युट्रल के साथ या पूर्व आवंटन के बिना?
स्टीवन सुदित

पूर्व-आवंटन के साथ, यह अभी भी चार [] आवंटन और नए स्ट्रिंग की तुलना में 40% धीमा है।
user7116

2
यह मुझे पंसद है। मैंने इस पद्धति को foreach (char c in input.Where(c => char.IsLetterOrDigit(c) || allowedSpecialCharacters.Any(x => x == c))) buffer[idx++] = c;
ट्विक किया

11

एक नियमित अभिव्यक्ति की तरह दिखेगा:

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

लेकिन अगर प्रदर्शन अत्यधिक महत्वपूर्ण है, तो मैं आपको "रेगेक्स पथ" चुनने से पहले कुछ बेंचमार्क करने की सलाह देता हूं ...


11

यदि आप वर्णों की डायनामिक सूची का उपयोग कर रहे हैं, तो LINQ बहुत तेज़ और सुंदर समाधान प्रस्तुत कर सकता है:

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

मैंने इस दृष्टिकोण की तुलना पिछले "तेज़" दृष्टिकोणों (रिलीज़ संकलन) में से दो के खिलाफ की है:

  • ल्यूक द्वारा 4 सरणी समाधान - 427 एमएस
  • StringBuilder समाधान - 429 एमएस
  • LINQ (इस उत्तर) - 98 एमएस

ध्यान दें कि एल्गोरिथ्म थोड़ा संशोधित है - पात्रों को हार्ड-कोडेड के बजाय एक सरणी के रूप में पारित किया जाता है, जो कि चीजों को थोड़ा प्रभावित कर सकता है (यानी / अन्य समाधानों में चरित्र सरणी की जांच करने के लिए एक आंतरिक लूप लूप होगा)।

यदि मैं एक कठिन-कोडित समाधान का उपयोग करके एक LINQ का उपयोग करता हूं जहां खंड, परिणाम हैं:

  • चार सरणी समाधान - 7ms
  • StringBuilder समाधान - 22ms
  • LINQ - 60 एमएस

यदि आप वर्णों की सूची को हार्ड-कोडिंग के बजाय अधिक सामान्य समाधान लिखने की योजना बना रहे हैं, तो आप LINQ या एक संशोधित दृष्टिकोण को देखने के लायक हो सकते हैं। LINQ निश्चित रूप से आपको संक्षिप्त, अत्यधिक पठनीय कोड देता है - रेगेक्स से भी अधिक।


3
यह दृष्टिकोण अच्छा लग रहा है, लेकिन यह काम नहीं करता है - सिवाय () एक सेट ऑपरेशन है, इसलिए आप स्ट्रिंग में प्रत्येक अद्वितीय चरित्र के केवल पहले स्वरूप के साथ समाप्त हो जाएंगे।
McKenzieG1

5

मुझे यकीन नहीं है कि आपका एल्गोरिथ्म कुछ भी है लेकिन कुशल है। यह O (n) है और केवल प्रत्येक वर्ण को एक बार देखता है। जब तक आप जादुई तरीके से मूल्यों की जाँच करने से पहले उन्हें नहीं जान लेते, आप इससे बेहतर नहीं होंगे।

मैं फिर भी StringBuilderस्ट्रिंग के प्रारंभिक आकार के लिए आपकी क्षमता को इनिशियलाइज़ करूँगा । मैं अनुमान लगा रहा हूं कि आपकी कथित प्रदर्शन समस्या मेमोरी रीअलोकेशन से आती है।

साइड नोट: चेकिंग A- zसुरक्षित नहीं है। आप शामिल कर रहे हैं [, \, ], ^, _, और `...

साइड नोट 2: दक्षता के उस अतिरिक्त बिट के लिए, तुलनाओं की संख्या को कम करने के लिए तुलनाओं को एक क्रम में रखें। (सबसे खराब बात, आप 8 तुलनाओं के बारे में बात कर रहे हैं, इसलिए बहुत मुश्किल मत सोचो।) यह आपके अपेक्षित इनपुट के साथ बदलता है, लेकिन एक उदाहरण यह हो सकता है:

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

साइड नोट 3: यदि किसी भी कारण से आपको वास्तव में तेज़ होने की आवश्यकता है, तो स्विच स्टेटमेंट तेज़ हो सकता है। कंपाइलर को आपके लिए एक जंप टेबल बनाना चाहिए, जिसके परिणामस्वरूप केवल एक ही तुलना हो सकती है:

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

1
मैं मानता हूं कि आप इस पर O (n) को हरा नहीं सकते। हालांकि, प्रति तुलना लागत है जिसे कम किया जा सकता है। एक टेबल लुकअप की कम, निश्चित लागत है, जबकि तुलनाओं की एक श्रृंखला लागत में वृद्धि करने जा रही है क्योंकि आप अधिक अपवाद जोड़ते हैं।
स्टीवन सुदित

साइड नोट 3 के बारे में, क्या आपको वास्तव में लगता है कि जंप टेबल टेबल लुकअप से तेज होगी?
स्टीवन सुदित

मैंने स्विच समाधान पर त्वरित प्रदर्शन परीक्षण चलाया, और यह तुलना के समान प्रदर्शन करता है।
गुफा

@Steven Sudit - मैं उद्यम करूंगा कि वे वास्तव में उसी के बारे में हैं। परीक्षण चलाने के लिए देखभाल?
एलसी।

7
O (n) नोटेशन कभी-कभी मुझे पेशाब कर देता है। लोग इस तथ्य के आधार पर बेवकूफ धारणाएं बनाएंगे कि एल्गोरिथ्म पहले से ही ओ (एन) है। अगर हमने str [i] कॉल को बदलने के लिए इस रूटीन को बदल दिया है, तो दुनिया के विपरीत दिशा में एक सर्वर के साथ एक बार एसएसएल कनेक्शन का निर्माण करके तुलना मूल्य को पुनः प्राप्त करने के लिए कॉल किया जाता है ... आप निश्चित रूप से एक बड़ा प्रदर्शन देखेंगे अंतर और एल्गोरिथ्म STILL O (n) है। प्रत्येक एल्गोरिथ्म के लिए O (1) की लागत महत्वपूर्ण और समकक्ष नहीं है!
डार्रोन



3

यह मुझे अच्छा लगता है। एकमात्र सुधार मैं StringBuilderस्ट्रिंग की लंबाई के साथ शुरू करना है ।

StringBuilder sb = new StringBuilder(str.Length);

3

मैं इस कोड नमूने से सहमत हूं। केवल एक ही मैं इसे स्ट्रिंग प्रकार के एक्सटेंशन विधि में बनाता हूं। ताकि आप इसे बहुत ही सरल रेखा या कोड में उपयोग कर सकें:

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

आपके प्रयोग के लिए गुफ़ा को धन्यवाद।

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

मैं एक स्ट्रिंग को रिप्लेस करने के साथ एक रेगुलर एक्सप्रेशन के साथ "स्पेशल कैरेक्टर्स" की खोज करूंगा, जो सभी कैरेक्टर्स को खाली स्ट्रिंग के साथ रिप्लेस करेगा।


+1 निश्चित रूप से कम कोड और यकीनन अधिक पठनीय अनदेखी लिखना-रेगेक्स।
केनी

1
@kenny - मैं सहमत हूँ। मूल प्रश्न यहां तक ​​कहा गया है कि तार छोटे हैं - 10-30 वर्ण। लेकिन जाहिरा तौर पर बहुत से लोग अभी भी सोचते हैं कि हम CPU समय को दूसरे द्वारा बेच रहे हैं ...
टॉम Bushell

शासक एक्सप्रेसिन बहुत आलसी काम करता है। इसलिए इसका उपयोग हमेशा नहीं किया जाना चाहिए।
रॉकऑनगोम

2

मुझे काम के लिए कुछ ऐसा ही करना था, लेकिन मेरे मामले में मुझे वह सब फ़िल्टर करना था जो एक अक्षर, संख्या या व्हाट्सएप नहीं है (लेकिन आप इसे आसानी से अपनी आवश्यकताओं के लिए संशोधित कर सकते हैं)। फ़िल्टरिंग क्लाइंट-साइड जावास्क्रिप्ट में किया जाता है, लेकिन सुरक्षा कारणों से मैं फ़िल्टरिंग सर्वर-साइड भी कर रहा हूं। चूंकि मैं अधिकांश तारों को साफ होने की उम्मीद कर सकता हूं, इसलिए मैं स्ट्रिंग की प्रतिलिपि बनाने से बचना चाहूंगा जब तक कि मुझे वास्तव में आवश्यकता न हो। यह मुझे नीचे लागू करने की अनुमति देता है, जो स्वच्छ और गंदे दोनों तारों के लिए बेहतर प्रदर्शन करना चाहिए।

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

एस एंड जी के लिए, लिनक-इफाइड तरीका:

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

मुझे नहीं लगता कि यह सबसे कुशल तरीका होने जा रहा है, हालांकि।


2
यह नहीं है, क्योंकि यह एक रैखिक खोज है।
स्टीवन सुदित

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

और आपको एक साफ तार मिलेगा s

erase()यह सभी विशेष पात्रों की पट्टी करेगा और my_predicate()फ़ंक्शन के साथ उच्च अनुकूलन योग्य है ।


1

HashSet O (1)
निश्चित नहीं है अगर यह मौजूदा तुलना से तेज है

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

मैंने परीक्षण किया और यह स्वीकार किए गए उत्तर की तुलना में तेज़ नहीं है।
मैं इसे छोड़ दूंगा जैसे कि आपको वर्णों के एक विन्यास योग्य सेट की आवश्यकता है यह एक अच्छा समाधान होगा।


आपको क्यों लगता है कि तुलना ओ (1) नहीं है?
गफ्फा

@ गुफ़ा मुझे यकीन नहीं है कि यह नहीं है और मैंने अपनी टिप्पणी हटा दी है। और +1। मुझे टिप्पणी करने से पहले और परीक्षण करना चाहिए था।
पापाराज़ो

1

मुझे आश्चर्य है कि अगर एक रेगेक्स-आधारित प्रतिस्थापन (संभवतः संकलित) तेज है। परीक्षण करना होगा कि किसी ने इसे ~ 5 गुना धीमा पाया है।

इसके अलावा, आपको StringBuilder को एक अपेक्षित लंबाई के साथ आरंभ करना चाहिए, ताकि मध्यवर्ती स्ट्रिंग को बढ़ने के दौरान चारों ओर से कॉपी न करना पड़े।

एक अच्छी संख्या मूल स्ट्रिंग की लंबाई है, या कुछ कम है (फ़ंक्शन इनपुट की प्रकृति के आधार पर)।

अंत में, आप यह देखने के लिए कि क्या किसी चरित्र को स्वीकार किया जाना है, एक लुकअप तालिका (रेंज 0..127 में) का उपयोग कर सकते हैं।


एक नियमित अभिव्यक्ति का परीक्षण पहले ही किया जा चुका है, और यह लगभग पाँच गुना धीमा है। रेंज में लुकअप टेबल के साथ 0..127 आपको लुकअप टेबल का उपयोग करने से पहले अभी भी वर्ण कोड की जांच करनी होगी, क्योंकि वर्ण 16 बिट मान हैं, न कि 7 बिट मान।
गुफ़ा

@ गुफ़ा इरर ... हाँ? ;)
क्रिश्चियन क्लासर

1

निम्नलिखित कोड में निम्नलिखित आउटपुट हैं (निष्कर्ष यह है कि हम कुछ स्मृति संसाधनों को सरणी के छोटे आकार को आवंटित करने से भी बचा सकते हैं):

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  

रूसी लोकेल का समर्थन करने के लिए आप निम्न कोड लाइनें भी जोड़ सकते हैं (सरणी का आकार 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

मुझे यकीन नहीं है कि यह सबसे कुशल तरीका है, लेकिन यह मेरे लिए काम करता है

 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

जवाब काम करता है , लेकिन सवाल सी # के लिए था (पुनश्च: मुझे पता है कि यह व्यावहारिक रूप से पांच साल पहले था, लेकिन अभी भी ..) मैंने टेलरीक वीबी का उपयोग सी # कनवर्टर, (और इसके विपरीत) के लिए किया था और कोड ने ठीक काम किया - किसी और के बारे में निश्चित नहीं, हालांकि। (एक और बात, converter.telerik.com )
Momoro

1

यहां बहुत सारे प्रस्तावित समाधान हैं, कुछ दूसरों की तुलना में अधिक कुशल हैं, लेकिन शायद बहुत पठनीय नहीं हैं। यहाँ एक है जो सबसे कुशल नहीं हो सकता है, लेकिन निश्चित रूप से ज्यादातर स्थितियों के लिए उपयोग करने योग्य है, और काफी संक्षिप्त और पठनीय है, लीवर 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
मुझे डर replaceAllहै कि C # स्ट्रिंग फ़ंक्शन नहीं है, लेकिन जावा या जावास्क्रिप्ट
Csaba Toth

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

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

उत्तर गलत है। यदि आप रेगेक्स का उपयोग करने जा रहे हैं, तो यह समावेशी होना चाहिए, विशेष रूप से नहीं, क्योंकि अब आप कुछ पात्रों को याद करते हैं। दरअसल, रेगेक्स के साथ पहले से ही उत्तर है। और पूर्ण होने के लिए - regex SLOWER है तो प्रत्यक्ष तुलना वर्ण फ़ंक्शन से करें।
TPAKTOPA

-3

यदि आप गति के बारे में चिंतित हैं, तो मौजूदा स्ट्रिंग को संपादित करने के लिए पॉइंटर्स का उपयोग करें। आप स्ट्रिंग को पिन कर सकते हैं और इसे एक पॉइंटर प्राप्त कर सकते हैं, फिर प्रत्येक चरित्र पर लूप के लिए चला सकते हैं, प्रत्येक अमान्य चरित्र को एक प्रतिस्थापन चरित्र के साथ लिख सकते हैं। यह अत्यंत कुशल होगा और इसे किसी नई स्ट्रिंग मेमोरी को आवंटित करने की आवश्यकता नहीं होगी। आपको असुरक्षित विकल्प के साथ अपने मॉड्यूल को भी संकलित करना होगा, और पॉइंटर्स का उपयोग करने के लिए अपने तरीके के हेडर में "असुरक्षित" संशोधक जोड़ना होगा।

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
Noooooooooo! .NET में स्ट्रिंग बदलना BAAAAAAAAAAD है! ढांचे में सब कुछ नियम पर निर्भर करता है कि तार अपरिवर्तनीय हैं, और यदि आप तोड़ते हैं तो आप बहुत आश्चर्यजनक दुष्प्रभाव प्राप्त कर सकते हैं ...
गुफा जूल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.