سؤال

هل هناك فئة C++ Standard Template Library التي توفر وظيفة تسلسل سلسلة فعالة، على غرار C#؟ StringBuilder أو جافا StringBuffer?

هل كانت مفيدة؟

المحلول

لاحظ أن هذه الإجابة قد حظيت ببعض الاهتمام مؤخرًا.أنا لا أدافع عن هذا كحل (لقد رأيت هذا الحل في الماضي، قبل المحكمة الخاصة بلبنان).إنه نهج مثير للاهتمام ويجب تطبيقه فقط std::string أو std::stringstream إذا اكتشفت بعد تصنيف الكود الخاص بك أن هذا يؤدي إلى تحسن.

أنا عادة استخدام إما std::string أو std::stringstream.لم يكن لدي أي مشاكل مع هذه.عادةً ما أقوم بحجز بعض المساحة أولاً إذا كنت أعرف الحجم التقريبي للسلسلة مسبقًا.

لقد رأيت أشخاصًا آخرين يصنعون أداة إنشاء السلسلة المُحسّنة الخاصة بهم في الماضي البعيد.

class StringBuilder {
private:
    std::string main;
    std::string scratch;

    const std::string::size_type ScratchSize = 1024;  // or some other arbitrary number

public:
    StringBuilder & append(const std::string & str) {
        scratch.append(str);
        if (scratch.size() > ScratchSize) {
            main.append(scratch);
            scratch.resize(0);
        }
        return *this;
    }

    const std::string & str() {
        if (scratch.size() > 0) {
            main.append(scratch);
            scratch.resize(0);
        }
        return main;
    }
};

يستخدم سلسلتين واحدة لغالبية السلسلة والأخرى كمنطقة خدش لتسلسل السلاسل القصيرة.يقوم بتحسين الإلحاق عن طريق تجميع عمليات الإلحاق القصيرة في سلسلة صغيرة واحدة ثم إلحاقها بالسلسلة الرئيسية، وبالتالي تقليل عدد عمليات إعادة التخصيص المطلوبة على السلسلة الرئيسية عندما تصبح أكبر.

لم أطلب هذه الخدعة مع std::string أو std::stringstream.أعتقد أنه تم استخدامه مع مكتبة سلسلة تابعة لجهة خارجية قبل std::string، كان ذلك منذ فترة طويلة.إذا كنت تعتمد استراتيجية مثل هذا الملف الشخصي، فإن طلبك أولاً.

نصائح أخرى

وسيلة وC ++ سيكون لاستخدام الأمراض المنقولة جنسيا :: stringstream مجرد أو تسلسالت السلسلة. سلاسل C ++ هي قابلة للتغيير حتى الاعتبارات أداء سلسلة أقل من القلق.

وفيما يتعلق التنسيق، يمكنك أن تفعل كل نفس التنسيق على تيار، ولكن في بطريقة مختلفة، على غرار cout . أو يمكنك استخدام functor كتابة بشدة الذي يغلف هذا ويوفر String.Format مثل واجهة على سبيل المثال دفعة :: شكل

وظيفة الأمراض المنقولة جنسيا :: string.append ليست خيارا جيدا لأنه لا يقبل العديد من أشكال البيانات. وثمة بديل أكثر فائدة هو استخدام الأمراض المنقولة جنسيا: stringstream، كما يلي:

#include <sstream>
// ...

std::stringstream ss;

//put arbitrary formatted data into the stream
ss << 4.5 << ", " << 4 << " whatever";

//convert the stream buffer into a string
std::string str = ss.str();

وstd::string <م> غير وC ++ يعادل: انها قابلة للتغيير

ويمكنك استخدام .append () لمجرد وصل سلاسل.

std::string s = "string1";
s.append("string2");

وأعتقد أنك قد تكون قادرة على القيام به:

std::string s = "string1";
s += "string2";

وأما بالنسبة للعمليات تنسيق من C # الصورة StringBuilder، أعتقد snprintf (أو sprintf إذا كنت ترغب في خطر كتابة التعليمات البرمجية عربات التي تجرها الدواب ؛-)) في صفيف حرف وتحويل مرة أخرى إلى سلسلة هو حول الخيار الوحيد.

ومنذ std::string في C ++ غير قابلة للتغيير يمكنك استخدام ذلك. أنه يحتوي على += operator وظيفة append.

إذا كنت بحاجة إلى إلحاق بيانات رقمية تستخدم وظائف std::to_string.

إذا كنت تريد المزيد من المرونة في شكل التمكن إجراء تسلسل أي كائن إلى سلسلة ثم استخدام الفئة std::stringstream. ولكن سوف تحتاج إلى تنفيذ المهام الخاصة مشغل يتدفقون من أجل أن العمل مع فئات المخصصة الخاصة بك.

والأمراض المنقولة جنسيا :: سلسلة ل+ = لا العمل مع CONST شار * (ما الاشياء مثل "سلسلة لإضافة" يبدو)، لذلك بالتأكيد باستخدام stringstream هو الأقرب إلى ما هو مطلوب - أنت مجرد استخدام << بدلا من +

منشئ سلسلة مناسب لـ c++

كما أجاب العديد من الأشخاص من قبل، std::stringstream هي الطريقة المفضلة.إنه يعمل بشكل جيد ويحتوي على الكثير من خيارات التحويل والتنسيق.المنظمة البحرية الدولية (IMO) بها عيب واحد غير مريح للغاية:لا يمكنك استخدامه كبطانة واحدة أو كتعبير.عليك دائما أن تكتب:

std::stringstream ss;
ss << "my data " << 42;
std::string myString( ss.str() );

وهو أمر مزعج للغاية، خاصة عندما تريد تهيئة السلاسل في المُنشئ.

والسبب هو أن أ) std::stringstream لا يوجد لديه عامل تحويل إلى std::string وb) لا يُرجع عامل التشغيل << () الخاص بـ stringstream مرجع سلسلة، ولكن مرجع std::ostream بدلاً من ذلك - والتي لا يمكن حسابها بشكل أكبر كتدفق سلسلة.

الحل هو تجاوز std::stringstream ومنحه عوامل تشغيل أفضل:

namespace NsStringBuilder {
template<typename T> class basic_stringstream : public std::basic_stringstream<T>
{
public:
    basic_stringstream() {}

    operator const std::basic_string<T> () const                                { return std::basic_stringstream<T>::str();                     }
    basic_stringstream<T>& operator<<   (bool _val)                             { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (char _val)                             { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (signed char _val)                      { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned char _val)                    { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (short _val)                            { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned short _val)                   { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (int _val)                              { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned int _val)                     { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (long _val)                             { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned long _val)                    { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (long long _val)                        { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned long long _val)               { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (float _val)                            { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (double _val)                           { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (long double _val)                      { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (void* _val)                            { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (std::streambuf* _val)                  { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (std::ostream& (*_val)(std::ostream&))  { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (std::ios& (*_val)(std::ios&))          { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (std::ios_base& (*_val)(std::ios_base&)){ std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (const T* _val)                         { return static_cast<basic_stringstream<T>&>(std::operator << (*this,_val)); }
    basic_stringstream<T>& operator<<   (const std::basic_string<T>& _val)      { return static_cast<basic_stringstream<T>&>(std::operator << (*this,_val.c_str())); }
};

typedef basic_stringstream<char>        stringstream;
typedef basic_stringstream<wchar_t>     wstringstream;
}

مع هذا، يمكنك كتابة أشياء مثل

std::string myString( NsStringBuilder::stringstream() << "my data " << 42 )

حتى في المنشئ.

يجب أن أعترف أنني لم أقم بقياس الأداء، لأنني لم أستخدمه في بيئة تستخدم بناء السلسلة بكثافة حتى الآن، لكنني أفترض أنه لن يكون أسوأ بكثير من std::stringstream، لأن كل شيء قد تم عبر المراجع (باستثناء التحويل إلى سلسلة، ولكن هذه عملية نسخ في std::stringstream أيضًا)

حبل حاوية قيمتها اذا كانت لديك لإدراج / حذف السلسلة في في مكان عشوائي من سلسلة جهة أو لتسلسل شار طويلة. هنا مثال من تنفيذ SGI ل:

crope r(1000000, 'x');          // crope is rope<char>. wrope is rope<wchar_t>
                                // Builds a rope containing a million 'x's.
                                // Takes much less than a MB, since the
                                // different pieces are shared.
crope r2 = r + "abc" + r;       // concatenation; takes on the order of 100s
                                // of machine instructions; fast
crope r3 = r2.substr(1000000, 3);       // yields "abc"; fast.
crope r4 = r2.substr(1000000, 1000000); // also fast.
reverse(r2.mutable_begin(), r2.mutable_end());
                                // correct, but slow; may take a
                                // minute or more.

أردت أن أضيف شيئا جديدا للأسباب التالية:

في المحاولة الأولى فشلت في الفوز

std::ostringstreamoperator<<

الكفاءة، ولكن مع المزيد من المحاولات، تمكنت من إنشاء StringBuilder بشكل أسرع في بعض الحالات.

في كل مرة أقوم فيها بإلحاق سلسلة، أقوم فقط بتخزين إشارة إليها في مكان ما وزيادة عداد الحجم الإجمالي.

الطريقة الحقيقية التي قمت بتنفيذها أخيرًا (رعب!) هي استخدام مخزن مؤقت غير شفاف (std::vector < char > ):

  • رأس بايت واحد (بتان لمعرفة ما إذا كانت البيانات التالية: سلسلة منقولة أم سلسلة أم بايت[])
  • 6 بتات لمعرفة طول البايت[]

للبايت [ ]

  • أقوم بتخزين بايتات من السلاسل القصيرة مباشرةً (للوصول إلى الذاكرة التسلسلية)

للسلاسل المنقولة (السلاسل ملحقة بـ std::move)

  • المؤشر إلى أ std::string كائن (لدينا ملكية)
  • قم بتعيين علامة في الفصل إذا كانت هناك بايتات محجوزة غير مستخدمة هناك

للسلاسل

  • المؤشر إلى أ std::string كائن (بدون ملكية)

هناك أيضًا تحسين واحد صغير، إذا تم نقل آخر سلسلة مدرجة، فإنه يتحقق من البايتات المحجوزة مجانًا ولكن غير المستخدمة ويخزن المزيد من البايتات هناك بدلاً من استخدام المخزن المؤقت غير الشفاف (وهذا لحفظ بعض الذاكرة، فهو في الواقع يجعلها أبطأ قليلاً ، ربما تعتمد أيضًا على وحدة المعالجة المركزية، ومن النادر رؤية سلاسل ذات مساحة محجوزة إضافية على أي حال)

كان هذا أخيرًا أسرع قليلاً من std::ostringstream ولكن لديها بعض السلبيات:

  • لقد افترضت أن أنواع الأحرف ذات طول ثابت (لذلك 1،2 أو 4 بايت، ليست جيدة لـ UTF8)، أنا لا أقول أنها لن تعمل مع UTF8، فقط لم أتحقق منها بحثًا عن الكسل.
  • لقد استخدمت ممارسات ترميز سيئة (مخزن مؤقت غير شفاف، من السهل جعله غير محمول، وأعتقد أن برنامجي محمول بالمناسبة)
  • يفتقر إلى كافة الميزات ostringstream
  • إذا تم حذف بعض السلسلة المشار إليها قبل دمج جميع السلاسل:سلوك غير محدد.

خاتمة؟يستخدمstd::ostringstream

لقد قام بالفعل بإصلاح أكبر عنق الزجاجة في حين أن الحصول على نقاط مئوية قليلة في السرعة من خلال تنفيذ الألغام لا يستحق الجوانب السلبية.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top