c++ - Std:: স্ট্রিং ট্রিম করার সেরা উপায় কি?




trim stdstring (20)

আমি বর্তমানে আমার প্রোগ্রামগুলিতে সকল std::strings ডান-টিম করার জন্য নিচের কোডটি ব্যবহার করছি:

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

এটা জরিমানা কাজ করে, কিন্তু কিছু শেষ ক্ষেত্রে যেখানে এটি ব্যর্থ হতে পারে কি আমি আশ্চর্য?

অবশ্যই, মার্জিত বিকল্প এবং বাম trim সমাধান সঙ্গে উত্তর স্বাগত জানাই।


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

Trim সি ++ 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());
}

আমার উত্তরটি এই পোস্টের উপরের উত্তরটিতে একটি উন্নতি যা trims অক্ষর এবং স্পেসগুলি ( ASCII টেবিলে 0-32 এবং 127) নিয়ন্ত্রণ করে।

std::isgraph নির্ধারণ করে যে কোন চরিত্রটির গ্রাফিকাল উপস্থাপনা আছে, তাই আপনি 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::iswgraph যদি আপনার ব্যাপক ক্যারেক্টারের জন্য সমর্থন প্রয়োজন, তবে আপনাকে std::wstring ম্যানিপুলেশন সক্ষম করতে এই কোডটি সম্পাদনা করতে হবে, যা আমি পরীক্ষা করে দেখিনি (দেখুন এই বিকল্পটি অন্বেষণ করতে std::basic_string রেফারেন্স পৃষ্ঠা)।


আমার সমাধান @ বিল দ্য লিজার দ্বারা উত্তর উপর ভিত্তি করে।

উল্লেখ্য, ইনপুট স্ট্রিংটিতে হোয়াইটস্পেস থাকলে কিছুই নেই তবে এই ফাংশন খালি স্ট্রিংটি ফিরিয়ে দেবে।

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

আমি অনুমান করি যদি আপনি একটি স্ট্রিং টিম করার জন্য "সর্বোত্তম উপায়" চাইতে শুরু করেন তবে আমি বলব যে একটি ভাল বাস্তবায়ন এমন হবে যে:

  1. অস্থায়ী স্ট্রিং বরাদ্দ করা হয় না
  2. জায়গায় জায়গায় ছাঁটাই এবং কপি ছাঁটাই জন্য overloads আছে
  3. সহজে বিভিন্ন বৈধতা ক্রম / যুক্তিবিদ্যা গ্রহণ করতে অনুকূলিতকরণ করা যাবে

স্পষ্টতই এই পদ্ধতির সাথে যোগাযোগ করার অনেকগুলি ভিন্ন উপায় রয়েছে এবং এটি অবশ্যই আপনার যা দরকার তা নির্ভর করে। যাইহোক, সি স্ট্যান্ডার্ড লাইব্রেরির এখনও <string.h> তে কিছু খুব দরকারী ফাংশন রয়েছে, যেমন memchr। আইওর জন্য C কে এখনও সেরা ভাষা হিসেবে বিবেচনা করা হয় এমন একটি কারণ রয়েছে - এটির স্টললিবটি বিশুদ্ধ দক্ষতা।

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

আমি আপনার পরিবেশ একই কিনা নিশ্চিত নই, কিন্তু আমার খালি স্ট্রিং কেস প্রোগ্রাম বাতিল করা হবে। আমি যদি একটি if (! S.empty ()) এর সাথে মুছে ফেলার কলটি মোড়ানো করব অথবা ইতিমধ্যে উল্লিখিত বুস্ট ব্যবহার করব।


এই আমি ব্যবহার কি। শুধু সামনের দিক থেকে স্থান সরাতে থাকুন, এবং তারপর, যদি কিছু বাকি থাকে তবে ফিরে থেকে একই কাজ করুন।

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
}

এই চেষ্টা করুন, এটা আমার জন্য কাজ করে।

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

এই সম্পর্কে কি...?

#include <iostream>
#include <string>
#include <regex>

std::string ltrim( std::string str ) {
    return std::regex_replace( str, std::regex("^\\s+"), std::string("") );
}

std::string rtrim( std::string str ) {
    return std::regex_replace( str, std::regex("\\s+$"), std::string("") );
}

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

int main() {

    std::string str = "   \t  this is a test string  \n   ";
    std::cout << "-" << trim( str ) << "-\n";
    return 0;

}

দ্রষ্টব্য: আমি এখনও অপেক্ষাকৃত নতুন সি ++, তাই আমি এখানে বেস বন্ধ হলে আমাকে ক্ষমা করুন।


একটি খালি স্ট্রিংয়ের ক্ষেত্রে, আপনার কোড অনুমান করে যে 1 টি string::npos প্রদান করে 0। string::npos টাইপ string::size_type , যা স্বাক্ষরিত নয়। সুতরাং, আপনি অতিরিক্ত যোগফল আচরণ উপর নির্ভর করে।


এখানে আমি কি দিয়ে এসেছি:

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

প্রবাহ নিষ্কাশন স্বয়ংক্রিয়ভাবে হোয়াইটস্পেস নির্মূল করে, তাই এটি একটি কবজ মত কাজ করে।
খুব পরিষ্কার এবং মার্জিত খুব, আমি নিজেকে তাই বলে যদি। ;)


এটি করার একটি মার্জিত উপায় মত হতে পারে

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

গোলমাল আমার সমাধান অবদান। একটি নতুন স্ট্রিং তৈরি করতে এবং পরিবর্তিত একটিকে ফিরতে trim_in_place প্রদত্ত স্ট্রিংটি সংশোধন করার সময় trim_in_placetrim ফাংশন সি ++ 11 চালান semantics সমর্থন করে।

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

তবুও আরেকটি বিকল্প - উভয় প্রান্ত থেকে এক বা একাধিক অক্ষর মুছে ফেলে।

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

সি ++ 17 থেকে সম্পাদন করুন , স্ট্যান্ডার্ড লাইব্রেরির কিছু অংশ সরানো হয়েছে। সৌভাগ্যবশত, সি ++ 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://.com/a/44973498/524503 ধন্যবাদ।

মূল উত্তর:

আমি আমার trimming চাহিদা জন্য এই 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));
}

তারা মোটামুটি স্ব ব্যাখ্যামূলক এবং খুব ভাল কাজ।

std::ptr_fun , আমার std::ptr_funstd::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;
}

যদিও প্রসঙ্গের জন্য এবং উচ্চ ভোটের উত্তরটি এখনও পাওয়া যায় তবুও আমি উপরের মূল উত্তরটি ধরে রেখেছি।


back() এবং pop_back() কারণে এটি C ++ 11 এ আরও সহজভাবে করা যেতে পারে।

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

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

বুস্ট এর স্ট্রিং অ্যালগরিদম ব্যবহার করা সহজ হবে:

#include <boost/algorithm/string.hpp>

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

str এখন এখন "hello world!" । এছাড়াও trim_left এবং trim , যা উভয় পক্ষের trims।

যদি আপনি উপরের ফাংশনের নামগুলির যেকোনো _copy যোগ করেন যেমন trim_copy , ফাংশন একটি রেফারেন্সের মাধ্যমে এটি সংশোধন করার পরিবর্তে স্ট্রিংটির একটি ছাঁটা কপিটি ফেরত দেবে।

যদি আপনি উপরের ফাংশনের নামগুলির মধ্যে trim_copy_if , যেমন trim_copy_if , আপনি কেবলমাত্র হোয়াইট trim_copy_if বিরোধিতায় আপনার কাস্টম পূর্বাভাসকে সন্তুষ্ট করে সমস্ত অক্ষরগুলি ট্রিম করতে পারেন।


আমি আমার পুরানো সি ++ ট্রিম ফাংশনটি সি ++ 11 পদ্ধতির সাথে আপডেট করতে চেয়েছিলাম তাই আমি প্রশ্নটির বেশিরভাগ উত্তর পরীক্ষা করেছি। আমার উপসংহার আমি আমার পুরানো সি ++ সমাধান রাখা!

এটি দ্রুততম দ্রুততম, এমনকি চেক করার জন্য আরো অক্ষর যুক্ত করে (যেমন \ r \ n আমি \ f \ v এর জন্য কোনও ব্যবহার কেস দেখি না) এখনও অ্যালগরিদম ব্যবহার করে সমাধানগুলির চেয়ে দ্রুত।

     std::string & trimMe (std::string & str)
     {
        // right trim
        while (str.length () > 0 && (str [str.length ()-1] == ' ' || str [str.length ()-1] == '\t'))
           str.erase (str.length ()-1, 1);

        // left trim
        while (str.length () > 0 && (str [0] == ' ' || str [0] == '\t'))
           str.erase (0, 1);
        return str;
     }

এখানে একটি সরাসরি এগিয়ে বাস্তবায়ন। যেমন একটি সহজ অপারেশন জন্য, আপনি সম্ভবত কোনো বিশেষ গঠন ব্যবহার করা উচিত নয়। বিল্ড ইন ইস্যেসেস () ফাংশন সাদা অক্ষরের বিভিন্ন রূপগুলির যত্ন নেয়, তাই আমাদের এটির সুবিধা নিতে হবে। আপনি বিশেষ ক্ষেত্রে বিবেচনা করতে হবে যেখানে স্ট্রিং খালি বা কেবল স্পেসগুলির একটি গুচ্ছ। বাম বা ডান ছাঁটাই নিম্নলিখিত কোড থেকে উদ্ভূত হতে পারে।

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




stdstring