Std :: string को ट्रिम करने का सबसे अच्छा तरीका क्या है?


812

मैं वर्तमान std::stringsमें अपने कार्यक्रमों में सभी को राइट-ट्रिम करने के लिए निम्न कोड का उपयोग कर रहा हूं :

std::string s;
s.erase(s.find_last_not_of(" \n\r\t")+1);

यह ठीक काम करता है, लेकिन मुझे आश्चर्य है कि अगर कुछ अंत-मामले हैं जहां यह विफल हो सकता है?

बेशक, सुरुचिपूर्ण विकल्प और बाएं-ट्रिम समाधान के साथ जवाब का स्वागत है।


549
इस प्रश्न के उत्तर C ++ मानक पुस्तकालय की कमी है कि कैसे एक वसीयतनामा है।
इदं के

83
@IdanK और अभी भी C ++ 11 में यह फ़ंक्शन नहीं है।
क्वांटम

44
@ इदं: महान, है ना! सभी प्रतिस्पर्धात्मक विकल्पों को देखें जो हमारे पास अब हमारे निपटान में हैं, एक व्यक्ति के विचार " जिस तरह से हमें इसे करना चाहिए " से अप्रभावित है !
लाइटनेस दौड़ ऑर्बिट में

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

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

जवाबों:


648

EDIT c ++ 17 के बाद से, मानक पुस्तकालय के कुछ हिस्सों को हटा दिया गया था। सौभाग्य से, c ++ 11 से शुरू होकर, हमारे पास लैम्ब्डा है जो एक बेहतर समाधान है।

#include <algorithm> 
#include <cctype>
#include <locale>

// trim from start (in place)
static inline void ltrim(std::string &s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
        return !std::isspace(ch);
    }));
}

// trim from end (in place)
static inline void rtrim(std::string &s) {
    s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
        return !std::isspace(ch);
    }).base(), s.end());
}

// trim from both ends (in place)
static inline void trim(std::string &s) {
    ltrim(s);
    rtrim(s);
}

// trim from start (copying)
static inline std::string ltrim_copy(std::string s) {
    ltrim(s);
    return s;
}

// trim from end (copying)
static inline std::string rtrim_copy(std::string s) {
    rtrim(s);
    return s;
}

// trim from both ends (copying)
static inline std::string trim_copy(std::string s) {
    trim(s);
    return s;
}

आधुनिक समाधान लाने के लिए https://stackoverflow.com/a/44973498/524503 का धन्यवाद ।

मूल उत्तर:

मैं अपनी ट्रिमिंग आवश्यकताओं के लिए इन 3 में से एक का उपयोग करता हूं:

#include <algorithm> 
#include <functional> 
#include <cctype>
#include <locale>

// trim from start
static inline std::string &ltrim(std::string &s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(),
            std::not1(std::ptr_fun<int, int>(std::isspace))));
    return s;
}

// trim from end
static inline std::string &rtrim(std::string &s) {
    s.erase(std::find_if(s.rbegin(), s.rend(),
            std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
    return s;
}

// trim from both ends
static inline std::string &trim(std::string &s) {
    return ltrim(rtrim(s));
}

वे काफी आत्म व्याख्यात्मक हैं और बहुत अच्छी तरह से काम करते हैं।

संपादित करें : BTW, मैं std::ptr_funवहाँ में मदद करने के लिए है std::isspaceक्योंकि वहाँ वास्तव में एक दूसरी परिभाषा है जो स्थानों का समर्थन करता है। यह सिर्फ एक ही कास्ट हो सकता था, लेकिन मुझे यह पसंद है।

संपादित करें : संदर्भ द्वारा एक पैरामीटर को स्वीकार करने, संशोधित करने और इसे वापस करने के बारे में कुछ टिप्पणियों को संबोधित करने के लिए। मैं सहमत हूँ। एक कार्यान्वयन जिसे मैं पसंद करता हूं वह दो सेट कार्य करेगा, एक जगह के लिए और एक जो प्रतिलिपि बनाता है। उदाहरणों का एक बेहतर सेट होगा:

#include <algorithm> 
#include <functional> 
#include <cctype>
#include <locale>

// trim from start (in place)
static inline void ltrim(std::string &s) {
    s.erase(s.begin(), std::find_if(s.begin(), s.end(),
            std::not1(std::ptr_fun<int, int>(std::isspace))));
}

// trim from end (in place)
static inline void rtrim(std::string &s) {
    s.erase(std::find_if(s.rbegin(), s.rend(),
            std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
}

// trim from both ends (in place)
static inline void trim(std::string &s) {
    ltrim(s);
    rtrim(s);
}

// trim from start (copying)
static inline std::string ltrim_copy(std::string s) {
    ltrim(s);
    return s;
}

// trim from end (copying)
static inline std::string rtrim_copy(std::string s) {
    rtrim(s);
    return s;
}

// trim from both ends (copying)
static inline std::string trim_copy(std::string s) {
    trim(s);
    return s;
}

मैं मूल उत्तर को संदर्भ के लिए और उच्च मत वाले उत्तर को अभी भी उपलब्ध रखने के हित में रख रहा हूं।


28
यह कोड कुछ अंतर्राष्ट्रीय स्ट्रिंग्स (मेरे मामले में शिफ्ट-जिस, एक std :: string में संग्रहीत) पर विफल हो रहा था; मैंने boost::trimसमस्या को हल करने के लिए उपयोग किया।
टॉम

5
मैं संदर्भों के बजाय पॉइंटर्स का उपयोग करूँगा, ताकि कॉलपॉइंट से यह समझना बहुत आसान हो जाए कि ये फ़ंक्शन कॉपी बनाने के बजाय स्ट्रिंग को संपादित करते हैं।
मार्को लेओग्रांडे

3
ध्यान दें कि आइसस्पेस के साथ आप गैर-ASCII वर्णों के साथ आसानी से अपरिभाषित व्यवहार प्राप्त कर सकते हैं- stroched.com/view?id=49bf8b0759f0dd36dffdad47663ac69f
आर।

10
स्थैतिक क्यों? क्या यह वह जगह है जहाँ एक अनाम नाम स्थान पसंद किया जाएगा?
ट्रेवर हिक्की

3
@TrevorHickey, निश्चित रूप से, यदि आप चाहें तो आप एक अनाम नाम स्थान का उपयोग कर सकते हैं।
इवान टेरान

417

बूस्ट के स्ट्रिंग एल्गोरिदम का उपयोग करना सबसे आसान होगा:

#include <boost/algorithm/string.hpp>

std::string str("hello world! ");
boost::trim_right(str);

strअब है "hello world!"। वहाँ भी है trim_leftऔर trim, जो दोनों पक्षों को प्रभावित करता है।


यदि आप _copyउपरोक्त फ़ंक्शन नामों में से किसी में प्रत्यय जोड़ते हैं जैसेtrim_copy , तो फ़ंक्शन एक संदर्भ के माध्यम से इसे संशोधित करने के बजाय स्ट्रिंग की एक छंटनी की प्रति वापस कर देगा।

यदि आप _ifउपर्युक्त फ़ंक्शन नामों में से किसी में प्रत्यय जोड़ते हैं trim_copy_if, तो आप सभी वर्णों को ट्रिम कर सकते हैं, जो आपके कस्टम विधेय को संतुष्ट करता है, जैसा कि सिर्फ व्हाट्सएप के विपरीत है।


7
यह लोकेल पर निर्भर करता है। मेरे डिफ़ॉल्ट लोकेल (VS2005, en) का अर्थ है टैब, स्पेस, कैरिज रिटर्न, न्यूलाइन, वर्टिकल टैब और फॉर्म फीड ट्रिम कर दिए गए हैं।
MattyT

117
इस तरह की छोटी समस्या के लिए बूस्ट इतना बड़ा हथौड़ा है।
केसी रोडरमोर

143
@rodarmor: बूस्ट कई छोटी समस्याओं का हल करता है। यह एक विशाल हथौड़ा है जो बहुत हल करता है।
निकोल बोलस

123
बूस्ट कई अलग-अलग आकारों के हथौड़ों का एक सेट है जो कई अलग-अलग समस्याओं को हल करता है।
इब्राहिम

11
@rodarmor आप कहते हैं कि जैसे कि बूस्ट एक ऑल-एंड-नथिंग मोनोलिथ है, जहां इसके हेडर सहित किसी भी तरह किसी के कार्यक्रम पर पूरी चीज को संक्रमित करता है। जो स्पष्ट रूप से मामला नहीं है। Btw, मैंने कभी बूस्ट, fwiw का उपयोग नहीं किया है।
अंडरस्कोर_ड

61

std::strings( Ideone ) से राइट ट्रिम (अनुगामी) स्थान और टैब वर्णों के लिए निम्न कोड का उपयोग करें :

// trim trailing spaces
size_t endpos = str.find_last_not_of(" \t");
size_t startpos = str.find_first_not_of(" \t");
if( std::string::npos != endpos )
{
    str = str.substr( 0, endpos+1 );
    str = str.substr( startpos );
}
else {
    str.erase(std::remove(std::begin(str), std::end(str), ' '), std::end(str));
}

और चीजों को संतुलित करने के लिए, मैं बाएं ट्रिम कोड को भी शामिल करूंगा ( आइडोन ):

// trim leading spaces
size_t startpos = str.find_first_not_of(" \t");
if( string::npos != startpos )
{
    str = str.substr( startpos );
}

4
यह व्हॉट्सएप के अन्य रूपों का पता नहीं लगाएगा ... विशेष रूप से न्यूलाइन, लाइन फीड, कैरिज रिटर्न।
टॉम

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

5
str.substr(...).swap(str)बेहतर है। एक असाइनमेंट सहेजें।
updogliu

4
@updogliu क्या यह चाल असाइनमेंट का उपयोग नहीं करेगा basic_string& operator= (basic_string&& str) noexcept;?
नुरेटिन

8
यह उत्तर उन स्ट्रिंग्स को परिवर्तित नहीं करता है जो सभी रिक्त स्थान हैं। जो फेल है।
टॉम एंडरसन

56

आप जो कर रहे हैं वह ठीक और मजबूत है। मैंने लंबे समय से एक ही विधि का उपयोग किया है और मुझे अभी तक एक तेज़ विधि नहीं मिली है:

const char* ws = " \t\n\r\f\v";

// trim from end of string (right)
inline std::string& rtrim(std::string& s, const char* t = ws)
{
    s.erase(s.find_last_not_of(t) + 1);
    return s;
}

// trim from beginning of string (left)
inline std::string& ltrim(std::string& s, const char* t = ws)
{
    s.erase(0, s.find_first_not_of(t));
    return s;
}

// trim from both ends of string (right then left)
inline std::string& trim(std::string& s, const char* t = ws)
{
    return ltrim(rtrim(s, t), t);
}

छंटनी किए जाने वाले पात्रों की आपूर्ति करके आपके पास गैर-व्हाट्सएप वर्णों को ट्रिम करने की क्षमता है और केवल आपके द्वारा छंटे हुए पात्रों को ट्रिम करने की दक्षता है।


यदि आप क्रम बदलते हैं trim, तो इसे rtrim(ltrim(s, t), t)थोड़ा और अधिक कुशल बना देंगे
CITBL

1
@CITBL इनर फंक्शन पहले किया जाता है ताकि आपका रास्ता सही से ट्रिमिंग से पहले बाईं ओर से ट्रिम हो जाए। मुझे लगता है कि यह कम कुशल नहीं होगा?
गालिक

बिल्कुल सही। मेरी गलती
CITBL

यदि आप चारो तरफ बेसिक_स्ट्रिंग और टेम्पलेट का उपयोग करते हैं, तो आप इसे सभी स्ट्रिंग्स के लिए कर सकते हैं, बस व्हॉट्सएप के लिए एक टेम्प्लेट चर का उपयोग करें ताकि आप इसे ws <CharT> की तरह उपयोग करें। तकनीकी रूप से उस बिंदु पर आप इसे c ++ के लिए 20 के लिए तैयार कर सकते हैं और यह भी निशान constexpr इस इनलाइन का तात्पर्य के रूप में कर सकता है
समुद्रतटीय

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

55

पार्टी करने में थोड़ा देर हो गई, लेकिन कभी मन नहीं भरा। अब C ++ 11 यहाँ है, हमारे पास लैम्ब्डा और ऑटो चर हैं। तो मेरा संस्करण, जो सभी व्हाट्सएप और खाली तारों को भी संभालता है:

#include <cctype>
#include <string>
#include <algorithm>

inline std::string trim(const std::string &s)
{
   auto wsfront=std::find_if_not(s.begin(),s.end(),[](int c){return std::isspace(c);});
   auto wsback=std::find_if_not(s.rbegin(),s.rend(),[](int c){return std::isspace(c);}).base();
   return (wsback<=wsfront ? std::string() : std::string(wsfront,wsback));
}

हम एक रिवर्स इटरेटर बना सकते हैं wsfrontऔर दूसरे में समाप्ति की स्थिति के रूप में उपयोग कर सकते हैं, find_if_notलेकिन यह केवल एक ऑल-व्हाट्सएप स्ट्रिंग के मामले में उपयोगी है, और जीसीसी 4.8 कम से कम रिवर्स इटरेटर के प्रकार का पता लगाने के लिए पर्याप्त स्मार्ट नहीं है ( std::string::const_reverse_iterator) के साथ auto। मुझे नहीं पता कि एक रिवर्स इटरेटर का निर्माण कितना महंगा है, इसलिए यहाँ YMMV है। इस परिवर्तन के साथ, कोड इस तरह दिखता है:

inline std::string trim(const std::string &s)
{
   auto  wsfront=std::find_if_not(s.begin(),s.end(),[](int c){return std::isspace(c);});
   return std::string(wsfront,std::find_if_not(s.rbegin(),std::string::const_reverse_iterator(wsfront),[](int c){return std::isspace(c);}).base());
}

9
अच्छा लगा। मुझ से +1। बहुत बुरा C ++ 11 ने std :: string में ट्रिम () का परिचय नहीं दिया और सभी के लिए जीवन आसान बना दिया।
मिलान बाबूसकोव

3
मैं हमेशा एक फ़ंक्शन कॉल करना चाहता हूं ताकि इसे लागू करने के बजाय स्ट्रिंग को ट्रिम किया जा सके
अलंकृत करें

22
इसकी कीमत क्या है, इसके लिए उस मेमने का इस्तेमाल करने की कोई जरूरत नहीं है। आप बस पास कर सकते हैं std::isspace:auto wsfront=std::find_if_not(s.begin(),s.end(),std::isspace);
vmrob

4
+1 शायद कार्यान्वयन के साथ एकमात्र उत्तर है जो केवल एक O (N) स्ट्रिंग कॉपी करता है।
अलेक्सी एवरचेंको

4
@vmrob संकलक जरूरी नहीं कि स्मार्ट हो। आप जो कह रहे हैं वह अस्पष्ट है:candidate template ignored: couldn't infer template argument '_Predicate' find_if_not(_InputIterator __first, _InputIterator __last, _Predicate __pred)
जॉन डेकर

42

यह कोशिश करो, यह मेरे लिए काम करता है।

inline std::string trim(std::string& str)
{
    str.erase(0, str.find_first_not_of(' '));       //prefixing spaces
    str.erase(str.find_last_not_of(' ')+1);         //surfixing spaces
    return str;
}

12
यदि आपके स्ट्रिंग में कोई प्रत्यय रिक्त स्थान नहीं है, तो यह npos + 1 == 0 पर शुरू हो जाएगा, और आप पूरे स्ट्रिंग को हटा देंगे।
mhsmith

3
@rgove कृपया समझाएं। str.find_last_not_of(x)एक्स के बराबर नहीं पहले चरित्र की स्थिति देता है। यदि कोई चार्ट x से मेल नहीं खाता है तो यह केवल npos देता है। उदाहरण में, अगर कोई प्रत्यय रिक्त स्थान नहीं हैं, तो यह str.length() - 1अनिवार्य रूप से वापस आ जाएगा str.erase((str.length() - 1) + 1)., जब तक कि मुझे बहुत गलत नहीं किया जाता है।
ट्रैविस

5
यह प्रतिलिपि :: स्ट्रिंग और स्ट्रिंग को अनावश्यक रूप से प्रतिलिपि बनाने वाले से बचने के लिए वापस करना चाहिए।
हेकसांग

7
मैं उलझन में हूं कि यह रिटर्न पैरामीटर को संशोधित करने के बाद एक प्रति क्यों लौटाता है?
गलिक

3
@MiloDC मेरा भ्रम यह है कि एक संदर्भ के बजाय एक प्रति वापस क्यों करें । यह मेरे लिए और अधिक समझ में आता है std::string&
गैलिक

25

मुझे tzaman का समाधान पसंद है, इसके साथ एकमात्र समस्या यह है कि यह केवल रिक्त स्थान वाले स्ट्रिंग को ट्रिम नहीं करता है।

उस 1 दोष को ठीक करने के लिए, 2 ट्रिमर लाइनों के बीच एक str.clear () जोड़ें

std::stringstream trimmer;
trimmer << str;
str.clear();
trimmer >> str;

अच्छा :) हमारे दोनों समाधानों के साथ समस्या, हालांकि, यह है कि वे दोनों छोरों को ट्रिम कर देंगे; इस तरह एक ltrimया नहीं बना सकते हैं rtrim
तजमान

44
अच्छा है, लेकिन आंतरिक व्हाट्सएप के साथ स्ट्रिंग से निपट नहीं सकता है। जैसे ट्रिम (abc def ") -> abc, केवल abc बचा।
liheyuan

एक अच्छा समाधान यदि आप जानते हैं कि कोई भी आंतरिक व्हाट्सएप नहीं होगा!
इलियट गोरोखोवस्की

यह अच्छा और आसान है, लेकिन यह भी काफी धीमा है क्योंकि स्ट्रिंग को अंदर और बाहर नकल किया जाता है std::stringstream
गैलिक

23

http://ideone.com/nFVtEo

std::string trim(const std::string &s)
{
    std::string::const_iterator it = s.begin();
    while (it != s.end() && isspace(*it))
        it++;

    std::string::const_reverse_iterator rit = s.rbegin();
    while (rit.base() != it && isspace(*rit))
        rit++;

    return std::string(it, rit.base());
}

1
अंतिम स्थान पर मूल ट्रिम के लिए सुरुचिपूर्ण समाधान ... :)
jave.web

यह कैसे काम करता है: यह एक कॉपी-जैसा समाधान है - यह पहले वर्ण की स्थिति का पता लगाता है जो स्थान नहीं है ( it) और रिवर्स: चरित्र की स्थिति जिसके बाद केवल रिक्त स्थान हैं ( rit) - इसके बाद यह एक नया बनाया स्ट्रिंग == देता है मूल स्ट्रिंग के हिस्से की एक प्रति - उन
पुनरावृत्तियों

धन्यवाद, मेरे लिए काम किया: एसटीडी: स्ट्रिंग एस = "ओह नोज: स्पेस \ आर \ एन"; std :: string clean = trim (s);
एलेक्सी रोशे

15

खाली स्ट्रिंग के मामले में, आपका कोड मानता है कि 1 में string::npos0. जोड़ने के लिए string::nposटाइप है string::size_type, जो अहस्ताक्षरित है। इस प्रकार, आप जोड़ के अतिप्रवाह व्यवहार पर भरोसा कर रहे हैं।


23
आप इसे खराब मान रहे हैं। हस्ताक्षरित पूर्णांक अतिप्रवाह व्यवहार खराब है।
एमएसल्टर्स

2
जोड़ने 1के लिए std::string::npos चाहिए देने 0के अनुसार C++ Standard। तो यह एक अच्छी धारणा है जिस पर पूरी तरह भरोसा किया जा सकता है।
गालिक

13

Cplusplus.com से हैक किया गया

std::string choppa(const std::string &t, const std::string &ws)
{
    std::string str = t;
    size_t found;
    found = str.find_last_not_of(ws);
    if (found != std::string::npos)
        str.erase(found+1);
    else
        str.clear();            // str is all whitespace

    return str;
}

यह अशक्त मामले के लिए भी काम करता है। :-)


4
यह सिर्फ है rtrim, नहींltrim
ub3rst4r 7

1
^ क्या आपको find_first_not_of का उपयोग करने में कोई आपत्ति है? इसे संशोधित करना अपेक्षाकृत आसान है।
अभिनव गौनियाल

13

सी के साथ ++ 17 आप उपयोग कर सकते basic_string_view :: remove_prefix और basic_string_view :: remove_suffix :

std::string_view trim(std::string_view s)
{
    s.remove_prefix(std::min(s.find_first_not_of(" \t\r\v\n"), s.size()));
    s.remove_suffix(std::min(s.size() - s.find_last_not_of(" \t\r\v\n") - 1, s.size()));

    return s;
}

एक अच्छा विकल्प:

std::string_view ltrim(std::string_view s)
{
    s.remove_prefix(std::distance(s.cbegin(), std::find_if(s.cbegin(), s.cend(),
         [](int c) {return !std::isspace(c);})));

    return s;
}

std::string_view rtrim(std::string_view s)
{
    s.remove_suffix(std::distance(s.crbegin(), std::find_if(s.crbegin(), s.crend(),
        [](int c) {return !std::isspace(c);})));

    return s;
}

std::string_view trim(std::string_view s)
{
    return ltrim(rtrim(s));
}

मुझे यकीन है कि क्या आप नहीं परीक्षण कर रहे हैं रहा हूँ, लेकिन अपने उदाहरण में std :: find_first_not_of वापस आ जाएगी std :: स्ट्रिंग :: एनपीओ और std :: string_view :: आकार 4. वापस आ जाएगी न्यूनतम स्पष्ट रूप से चार है, तत्वों की संख्या होने के लिए std द्वारा हटाया गया : string_view :: remove_prefix । दोनों gcc 9.2 और clang
Phidelux

1
धन्यवाद! मुझे ठीक लगता है।
कंटैंगो

11

छिपकली @ के जवाब के आधार पर मेरा समाधान ।

ध्यान दें कि यदि इनपुट स्ट्रिंग में व्हाट्सएप के अलावा कुछ नहीं है तो ये फ़ंक्शन खाली स्ट्रिंग लौटा देंगे।

const std::string StringUtils::WHITESPACE = " \n\r\t";

std::string StringUtils::Trim(const std::string& s)
{
    return TrimRight(TrimLeft(s));
}

std::string StringUtils::TrimLeft(const std::string& s)
{
    size_t startpos = s.find_first_not_of(StringUtils::WHITESPACE);
    return (startpos == std::string::npos) ? "" : s.substr(startpos);
}

std::string StringUtils::TrimRight(const std::string& s)
{
    size_t endpos = s.find_last_not_of(StringUtils::WHITESPACE);
    return (endpos == std::string::npos) ? "" : s.substr(0, endpos+1);
}

9

मेरा उत्तर इस पोस्ट के लिए शीर्ष उत्तर पर एक सुधार है जो ट्रिम्स के साथ ही रिक्त स्थान (0-32 और 127 एसीसीसी टेबल पर ) को नियंत्रित करता है ।

std::isgraphयह निर्धारित करता है कि क्या किसी वर्ण का चित्रण प्रतिनिधित्व है, इसलिए आप किसी भी वर्ण को हटाने के लिए इवान के उत्तर को बदलने के लिए इसका उपयोग कर सकते हैं, जिसमें स्ट्रिंग के दोनों ओर से चित्रमय प्रतिनिधित्व नहीं है। परिणाम एक और अधिक सुंदर समाधान है:

#include <algorithm>
#include <functional>
#include <string>

/**
 * @brief Left Trim
 *
 * Trims whitespace from the left end of the provided std::string
 *
 * @param[out] s The std::string to trim
 *
 * @return The modified std::string&
 */
std::string& ltrim(std::string& s) {
  s.erase(s.begin(), std::find_if(s.begin(), s.end(),
    std::ptr_fun<int, int>(std::isgraph)));
  return s;
}

/**
 * @brief Right Trim
 *
 * Trims whitespace from the right end of the provided std::string
 *
 * @param[out] s The std::string to trim
 *
 * @return The modified std::string&
 */
std::string& rtrim(std::string& s) {
  s.erase(std::find_if(s.rbegin(), s.rend(),
    std::ptr_fun<int, int>(std::isgraph)).base(), s.end());
  return s;
}

/**
 * @brief Trim
 *
 * Trims whitespace from both ends of the provided std::string
 *
 * @param[out] s The std::string to trim
 *
 * @return The modified std::string&
 */
std::string& trim(std::string& s) {
  return ltrim(rtrim(s));
}

नोट: वैकल्पिक रूप से आपको उपयोग करने में सक्षम होना चाहिए std::iswgraphयदि आपको विस्तृत वर्णों के लिए समर्थन की आवश्यकता है, लेकिन std::wstringहेरफेर को सक्षम करने के लिए आपको इस कोड को संपादित करना होगा , जो कि मैंने परीक्षण नहीं किया है ( std::basic_stringइस विकल्प का पता लगाने के लिए संदर्भ पृष्ठ देखें ) ।


3
std :: ptr_fun को पदावनत कर दिया जाता है
johnbakers

8

C ++ 11 के साथ एक नियमित अभिव्यक्ति मॉड्यूल भी आया , जो निश्चित रूप से अग्रणी या अनुगामी रिक्त स्थान को ट्रिम करने के लिए इस्तेमाल किया जा सकता है।

शायद कुछ इस तरह:

std::string ltrim(const std::string& s)
{
    static const std::regex lws{"^[[:space:]]*", std::regex_constants::extended};
    return std::regex_replace(s, lws, "");
}

std::string rtrim(const std::string& s)
{
    static const std::regex tws{"[[:space:]]*$", std::regex_constants::extended};
    return std::regex_replace(s, tws, "");
}

std::string trim(const std::string& s)
{
    return ltrim(rtrim(s));
}

8

यही है वह जो मेरे द्वारा उपयोग किया जाता है। बस सामने से जगह को हटाते रहें, और फिर, अगर कुछ बचा है, तो पीछे से भी ऐसा ही करें।

void trim(string& s) {
    while(s.compare(0,1," ")==0)
        s.erase(s.begin()); // remove leading whitespaces
    while(s.size()>0 && s.compare(s.size()-1,1," ")==0)
        s.erase(s.end()-1); // remove trailing whitespaces
}

8
s.erase(0, s.find_first_not_of(" \n\r\t"));                                                                                               
s.erase(s.find_last_not_of(" \n\r\t")+1);   

2
यह थोड़ा और अधिक कुशल होगा यदि आप विपरीत क्रम में हैं और बाईं ओर ट्रिमिंग द्वारा एक पारी को लागू करने से पहले दाईं ओर से ट्रिम कर दें।
गैलिक

7

इसके लायक क्या है, यहां प्रदर्शन के लिए एक आंख के साथ एक ट्रिम कार्यान्वयन है। यह मैंने देखा है कई अन्य ट्रिम दिनचर्या की तुलना में बहुत तेज है। पुनरावृत्तियों और std :: पाता का उपयोग करने के बजाय, यह कच्चे c तार और सूचकांकों का उपयोग करता है। यह निम्नलिखित विशेष मामलों का अनुकूलन करता है: आकार 0 स्ट्रिंग (कुछ भी न करें), स्ट्रिंग को बिना व्हाट्सएप के ट्रिम (कुछ भी न करें), स्ट्रिंग को केवल अनुगामी व्हाट्सएप से ट्रिम करने के लिए (बस स्ट्रिंग का आकार बदलें), स्ट्रिंग जो पूरी तरह से व्हाट्सएप है (बस स्ट्रिंग को साफ़ करें) । और अंत में, सबसे खराब स्थिति में (अग्रणी व्हाट्सएप के साथ स्ट्रिंग), यह एक कुशल प्रतिलिपि निर्माण करने के लिए अपनी पूरी कोशिश करता है, केवल 1 प्रति का प्रदर्शन करता है और फिर मूल स्ट्रिंग के स्थान पर उस प्रतिलिपि को स्थानांतरित करता है।

void TrimString(std::string & str)
{ 
    if(str.empty())
        return;

    const auto pStr = str.c_str();

    size_t front = 0;
    while(front < str.length() && std::isspace(int(pStr[front]))) {++front;}

    size_t back = str.length();
    while(back > front && std::isspace(int(pStr[back-1]))) {--back;}

    if(0 == front)
    {
        if(back < str.length())
        {
            str.resize(back - front);
        }
    }
    else if(back <= front)
    {
        str.clear();
    }
    else
    {
        str = std::move(std::string(str.begin()+front, str.begin()+back));
    }
}

@bmgda शायद सैद्धांतिक रूप से सबसे तेज़ संस्करण हो सकता है कि यह हस्ताक्षर हो: "C" शून्य स्ट्रिंग_trim (char ** start_, char ** end_) ... मेरे बहाव को पकड़ो?

6

ऐसा करने का एक सुंदर तरीका हो सकता है

std::string & trim(std::string & str)
{
   return ltrim(rtrim(str));
}

और सहायक कार्यों के रूप में कार्यान्वित किया जाता है:

std::string & ltrim(std::string & str)
{
  auto it =  std::find_if( str.begin() , str.end() , [](char ch){ return !std::isspace<char>(ch , std::locale::classic() ) ; } );
  str.erase( str.begin() , it);
  return str;   
}

std::string & rtrim(std::string & str)
{
  auto it =  std::find_if( str.rbegin() , str.rend() , [](char ch){ return !std::isspace<char>(ch , std::locale::classic() ) ; } );
  str.erase( it.base() , str.end() );
  return str;   
}

और एक बार जब आप ये सब कर लेते हैं, तो आप इसे भी लिख सकते हैं:

std::string trim_copy(std::string const & str)
{
   auto s = str;
   return ltrim(rtrim(s));
}

6

ट्रिम सी ++ 11 कार्यान्वयन:

static void trim(std::string &s) {
     s.erase(s.begin(), std::find_if_not(s.begin(), s.end(), [](char c){ return std::isspace(c); }));
     s.erase(std::find_if_not(s.rbegin(), s.rend(), [](char c){ return std::isspace(c); }).base(), s.end());
}

5

मुझे लगता है कि यदि आप एक स्ट्रिंग को ट्रिम करने के लिए "सबसे अच्छा तरीका" पूछना शुरू करते हैं, तो मैं कहूंगा कि एक अच्छा कार्यान्वयन एक होगा:

  1. अस्थायी तार आवंटित नहीं करता है
  2. ट्रिम और कॉपी ट्रिम के लिए अधिभार है
  3. विभिन्न सत्यापन अनुक्रमों / तर्क को स्वीकार करने के लिए आसानी से अनुकूलित किया जा सकता है

जाहिर है कि इससे संपर्क करने के कई अलग-अलग तरीके हैं और यह निश्चित रूप से इस बात पर निर्भर करता है कि आपको वास्तव में क्या चाहिए। हालाँकि, सी मानक लाइब्रेरी में अभी भी कुछ बहुत उपयोगी कार्य <string.h> हैं, जैसे मेमचर। वहाँ एक कारण है कि सी अभी भी IO के लिए सबसे अच्छी भाषा के रूप में माना जाता है - इसकी stdlib शुद्ध दक्षता है।

inline const char* trim_start(const char* str)
{
    while (memchr(" \t\n\r", *str, 4))  ++str;
    return str;
}
inline const char* trim_end(const char* end)
{
    while (memchr(" \t\n\r", end[-1], 4)) --end;
    return end;
}
inline std::string trim(const char* buffer, int len) // trim a buffer (input?)
{
    return std::string(trim_start(buffer), trim_end(buffer + len));
}
inline void trim_inplace(std::string& str)
{
    str.assign(trim_start(str.c_str()),
        trim_end(str.c_str() + str.length()));
}

int main()
{
    char str [] = "\t \nhello\r \t \n";

    string trimmed = trim(str, strlen(str));
    cout << "'" << trimmed << "'" << endl;

    system("pause");
    return 0;
}

3

मुझे यकीन नहीं है कि यदि आपका वातावरण समान है, लेकिन मेरा, खाली स्ट्रिंग मामले के कारण कार्यक्रम निरस्त हो जाएगा। मैं या तो एक (यदि s .empty ()) के साथ कॉल मिटा देता हूं या पहले से बताए गए बूस्ट का उपयोग करता हूं।


3

यहाँ मैं क्या लेकर आया हूँ:

std::stringstream trimmer;
trimmer << str;
trimmer >> str;

स्ट्रीम निष्कर्षण व्हाट्सएप को स्वचालित रूप से समाप्त कर देता है, इसलिए यह एक आकर्षण की तरह काम करता है।
बहुत साफ और सुंदर भी, अगर मैं ऐसा खुद कहूं। ;)


15
हम्म; यह मानता है कि स्ट्रिंग में कोई आंतरिक व्हाट्सएप (जैसे रिक्त स्थान) नहीं है। ओपी ने केवल यह कहा कि वह बाईं या दाईं ओर व्हॉट्सएप ट्रिम करना चाहता था।
सुपरइलेक्ट्रिक

3

शोर के लिए मेरे समाधान का योगदान। trimएक नया स्ट्रिंग बनाने के लिए चूक और संशोधित स्ट्रिंग को वापस करते हुए trim_in_placeइसे पास किए गए स्ट्रिंग को संशोधित करता है। trimसमारोह का समर्थन करता है c ++ 11 कदम अर्थ विज्ञान।

#include <string>

// modifies input string, returns input

std::string& trim_left_in_place(std::string& str) {
    size_t i = 0;
    while(i < str.size() && isspace(str[i])) { ++i; };
    return str.erase(0, i);
}

std::string& trim_right_in_place(std::string& str) {
    size_t i = str.size();
    while(i > 0 && isspace(str[i - 1])) { --i; };
    return str.erase(i, str.size());
}

std::string& trim_in_place(std::string& str) {
    return trim_left_in_place(trim_right_in_place(str));
}

// returns newly created strings

std::string trim_right(std::string str) {
    return trim_right_in_place(str);
}

std::string trim_left(std::string str) {
    return trim_left_in_place(str);
}

std::string trim(std::string str) {
    return trim_left_in_place(trim_right_in_place(str));
}

#include <cassert>

int main() {

    std::string s1(" \t\r\n  ");
    std::string s2("  \r\nc");
    std::string s3("c \t");
    std::string s4("  \rc ");

    assert(trim(s1) == "");
    assert(trim(s2) == "c");
    assert(trim(s3) == "c");
    assert(trim(s4) == "c");

    assert(s1 == " \t\r\n  ");
    assert(s2 == "  \r\nc");
    assert(s3 == "c \t");
    assert(s4 == "  \rc ");

    assert(trim_in_place(s1) == "");
    assert(trim_in_place(s2) == "c");
    assert(trim_in_place(s3) == "c");
    assert(trim_in_place(s4) == "c");

    assert(s1 == "");
    assert(s2 == "c");
    assert(s3 == "c");
    assert(s4 == "c");  
}

3

यह सी ++ 11 में और अधिक बस के अलावा की वजह से किया जा सकता है back()और pop_back()

while ( !s.empty() && isspace(s.back()) ) s.pop_back();

ओपी द्वारा सुझाए गए दृष्टिकोण या तो बुरा नहीं है - केवल पालन करने के लिए थोड़ा कठिन है।
नोबार

3

यहाँ मेरा संस्करण है:

size_t beg = s.find_first_not_of(" \r\n");
return (beg == string::npos) ? "" : in.substr(beg, s.find_last_not_of(" \r\n") - beg);

आपको आखिरी पात्र याद आ रहा है। लंबाई में A +1 इसे हल करता है
galinette

2

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

string trim(char const *str)
{
  // Trim leading non-letters
  while(!isalnum(*str)) str++;

  // Trim trailing non-letters
  end = str + strlen(str) - 1;
  while(end > str && !isalnum(*end)) end--;

  return string(str, end+1);
}

2

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

string trimSpace(const string &str) {
   if (str.empty()) return str;
   string::size_type i,j;
   i=0;
   while (i<str.size() && isspace(str[i])) ++i;
   if (i == str.size())
      return string(); // empty string
   j = str.size() - 1;
   //while (j>0 && isspace(str[j])) --j; // the j>0 check is not needed
   while (isspace(str[j])) --j
   return str.substr(i, j-i+1);
}

2

यहाँ एक समाधान है जो शुरुआती लोगों के लिए std::हर जगह समझने के लिए आसान नहीं है और वे अभी तक परिचित नहीं हैं const, -सर्किट, iteratorएस, एसटीएल algorithm, आदि ...

#include <string>
#include <cctype> // for isspace
using namespace std;


// Left trim the given string ("  hello!  " --> "hello!  ")
string left_trim(string str) {
    int numStartSpaces = 0;
    for (int i = 0; i < str.length(); i++) {
        if (!isspace(str[i])) break;
        numStartSpaces++;
    }
    return str.substr(numStartSpaces);
}

// Right trim the given string ("  hello!  " --> "  hello!")
string right_trim(string str) {
    int numEndSpaces = 0;
    for (int i = str.length() - 1; i >= 0; i--) {
        if (!isspace(str[i])) break;
        numEndSpaces++;
    }
    return str.substr(0, str.length() - numEndSpaces);
}

// Left and right trim the given string ("  hello!  " --> "hello!")
string trim(string str) {
    return right_trim(left_trim(str));
}

आशा है ये मदद करेगा...


1

यह संस्करण आंतरिक व्हाट्सएप और गैर-अल्फ़ान्यूमेरिक्स को ट्रिम करता है:

static inline std::string &trimAll(std::string &s)
{   
    if(s.size() == 0)
    {
        return s;
    }

    int val = 0;
    for (int cur = 0; cur < s.size(); cur++)
    {
        if(s[cur] != ' ' && std::isalnum(s[cur]))
        {
            s[val] = s[cur];
            val++;
        }
    }
    s.resize(val);
    return s;
}

1

फिर भी एक अन्य विकल्प - दोनों सिरों से एक या अधिक वर्ण निकालता है।

string strip(const string& s, const string& chars=" ") {
    size_t begin = 0;
    size_t end = s.size()-1;
    for(; begin < s.size(); begin++)
        if(chars.find_first_of(s[begin]) == string::npos)
            break;
    for(; end > begin; end--)
        if(chars.find_first_of(s[end]) == string::npos)
            break;
    return s.substr(begin, end-begin+1);
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.