c++ - convert - كيفية تحويل سلسلة std:: إلى char*أو char*؟




convert string to char c++ (6)

كيف يمكنني تحويل std::string إلى char* أو const char* ؟

https://code.i-harness.com


C ++ 17

يغير C ++ 17 (القياسية القادمة) ملخص القالب basic_string مضيفا عدم تحميل زائد data() :

charT* data() noexcept;

الإرجاع: مؤشر p مثل p + i == & عامل التشغيل لكل i في [0، size ()].

CharT const * from std::basic_string<CharT>

std::string const cstr = { "..." };
char const * p = cstr.data(); // or .c_str()

CharT * من std::basic_string<CharT>

std::string str = { "..." };
char * p = str.data();

C ++ 11

CharT const * from std::basic_string<CharT>

std::string str = { "..." };
str.c_str();

CharT * من std::basic_string<CharT>

من C ++ 11 فصاعدًا ، يقول المعيار:

  1. يتم تخزين الكائنات المشابهة في كائن basic_string بشكل basic_string . وهذا يعني أنه بالنسبة لأي كائن s &*(s.begin() + n) == &*s.begin() + n ، يجب أن يحتفظ بالهوية &*(s.begin() + n) == &*s.begin() + n بجميع قيم n مثل 0 <= n < s.size() .
  1. const_reference operator[](size_type pos) const;
    reference operator[](size_type pos);

    الإرجاع: *(begin() + pos) إذا كانت pos < size() ، خلاف ذلك إشارة إلى كائن من نوع CharT بقيمة CharT() ؛ لا يجوز تعديل القيمة المرجعية.

  1. const charT* c_str() const noexcept;
    const charT* data() const noexcept;

    الإرجاع: مؤشر p مثل p + i == &operator[](i) لكل i في [0,size()] .

هناك طرق ممكنة قابلة للفصل للحصول على مؤشر حرف غير const.

1. استخدم التخزين المتجاوب لـ C ++ 11

std::string foo{"text"};
auto p = &*foo.begin();

طليعة

  • بسيطة وقصيرة
  • سريع (الطريقة الوحيدة دون مشاركة في النسخ)

سلبيات

  • لا يجب تغيير '\0' نهائيًا / ليس بالضرورة جزءًا من الذاكرة غير const.

2. استخدم std::vector<CharT>

std::string foo{"text"};
std::vector<char> fcv(foo.data(), foo.data()+foo.size()+1u);
auto p = fcv.data();

طليعة

  • بسيط
  • التعامل التلقائي مع الذاكرة
  • ديناميكي

سلبيات

  • يتطلب نسخة السلسلة

3. استخدم std::array<CharT, N> إذا كان N هو ثابت وقت تجميع (وصغير بما فيه الكفاية)

std::string foo{"text"};
std::array<char, 5u> fca;
std::copy(foo.data(), foo.data()+foo.size()+1u, fca.begin());

طليعة

  • بسيط
  • كومة الذاكرة التعامل

سلبيات

  • ثابتة
  • يتطلب نسخة السلسلة

4. تخصيص الذاكرة الخام مع حذف التخزين التلقائي

std::string foo{ "text" };
auto p = std::make_unique<char[]>(foo.size()+1u);
std::copy(foo.data(), foo.data() + foo.size() + 1u, &p[0]);

طليعة

  • بصمة ذاكرة صغيرة
  • الحذف التلقائي
  • بسيط

سلبيات

  • يتطلب نسخة السلسلة
  • ثابت (الاستخدام الديناميكي يتطلب الكثير من التعليمات البرمجية)
  • ميزات أقل من المتجه أو الصفيف

5. تخصيص الذاكرة الخام مع التعامل اليدوي

std::string foo{ "text" };
char * p = nullptr;
try
{
  p = new char[foo.size() + 1u];
  std::copy(foo.data(), foo.data() + foo.size() + 1u, p);
  // handle stuff with p
  delete[] p;
}
catch (...)
{
  if (p) { delete[] p; }
  throw;
}

طليعة

  • أقصى "السيطرة"

يخدع

  • يتطلب نسخة السلسلة
  • أقصى قدر من المسؤولية / القابلية للأخطاء
  • مركب

أنا أعمل مع API مع الكثير من الوظائف الحصول على إدخال char* .

لقد خلقت فئة صغيرة لمواجهة هذا النوع من المشاكل ، لقد نفذت لغة RAII.

class DeepString
{
        DeepString(const DeepString& other);
        DeepString& operator=(const DeepString& other);
        char* internal_; 

    public:
        explicit DeepString( const string& toCopy): 
            internal_(new char[toCopy.size()+1]) 
        {
            strcpy(internal_,toCopy.c_str());
        }
        ~DeepString() { delete[] internal_; }
        char* str() const { return internal_; }
        const char* c_str()  const { return internal_; }
};

ويمكنك استخدامه على النحو التالي:

void aFunctionAPI(char* input);

//  other stuff

aFunctionAPI("Foo"); //this call is not safe. if the function modified the 
                     //literal string the program will crash
std::string myFoo("Foo");
aFunctionAPI(myFoo.c_str()); //this is not compiling
aFunctionAPI(const_cast<char*>(myFoo.c_str())); //this is not safe std::string 
                                                //implement reference counting and 
                                                //it may change the value of other
                                                //strings as well.
DeepString myDeepFoo(myFoo);
aFunctionAPI(myFoo.str()); //this is fine

لقد اتصلت بالطبقة DeepString لأنها تقوم بإنشاء نسخة عميقة وفريدة (لا يمكن نسخ DeepString ) لسلسلة موجودة.


استخدم الأسلوب .c_str() لـ const char * .

يمكنك استخدام &mystring[0] للحصول على char * pointer ، ولكن هناك زوج من gotcha's: لن تحصل بالضرورة على سلسلة من صفر ، ولن تتمكن من تغيير حجم السلسلة. يجب عليك أن تكون حريصًا على عدم إضافة الأحرف بعد نهاية السلسلة أو ستحصل على تجاوز سعة المخزن المؤقت (والتعطل المحتمل).

لم يكن هناك أي ضمان بأن جميع الأحرف ستكون جزءًا من نفس المنطقة المؤقتة المتجاورة حتى C ++ 11 ، ولكن في الواقع العملي ، عملت جميع تطبيقات std::string المعروفة بهذه الطريقة على أي حال ؛ راجع هل تشير & "s [0] إلى الأحرف المتجاورة في سلسلة std ::؟ .

لاحظ أن العديد من وظائف عضو string سوف إعادة تخصيص المخزن المؤقت الداخلي وإبطال أي مؤشرات قد قمت بحفظها. من الأفضل استخدامها على الفور ثم تجاهلها.


القول مثلا ...

std::string x = "hello";

الحصول على `char *` أو `char char *` من `سلسلة`

كيفية الحصول على مؤشر حرف صالح بينما يظل x في النطاق ولم يتم تعديله بشكل أكبر

يعمل C ++ 11 على تبسيط الأشياء ؛ التالية كافة منح حق الوصول إلى نفس المخزن المؤقت السلسلة الداخلية:

const char* p_c_str = x.c_str();
const char* p_data  = x.data();
const char* p_x0    = &x[0];

      char* p_x0_rw = &x[0];  // compiles iff x is not const...

جميع المؤشرات المذكورة أعلاه سوف تحمل نفس القيمة - عنوان الحرف الأول في المخزن المؤقت. حتى السلسلة الفارغة لها "الحرف الأول في المخزن المؤقت" ، لأن C ++ 11 يضمن الاحتفاظ دائمًا بحرف نهاية NUL / 0 إضافي بعد محتوى السلسلة المخصص بشكل صريح (مثل std::string("this\0that", 9) سيكون لديك مخزن مؤقت يحمل "this\0that\0" ).

بالنظر إلى أي من المؤشرات المذكورة أعلاه:

char c = p[n];   // valid for n <= x.size()
                 // i.e. you can safely read the NUL at p[x.size()]

فقط من أجل المؤشر غير const من &x[0] :

p_x0_rw[n] = c;  // valid for n <= x.size() - 1
                 // i.e. don't overwrite the implementation maintained NUL

كتابة NUL في مكان آخر في السلسلة لا يغير size() string size() ؛ يسمح string أن تحتوي على أي عدد من NULs - فهي لا تعطى معاملة خاصة بواسطة std::string (في C ++ 03).

في C ++ 03 ، كانت الأمور أكثر تعقيدًا بشكل كبير (أبرز الاختلافات الرئيسية):

  • x.data()

    • إرجاع const char* إلى المخزن المؤقت الداخلي للسلسلة والذي لم يكن مطلوبًا من قبل المعيار لإبرامه مع NUL (أي قد يكون ['h', 'e', 'l', 'l', 'o'] متبوعاً بغير مهيأ أو قيم قمامة ، مع وصول عرضي لها وجود سلوك غير معروف ).
      • x.size() الأحرف آمنة للقراءة ، مثل x[0] خلال x[x.size() - 1]
      • بالنسبة إلى السلاسل الفارغة ، فأنت تضمن بعض المؤشرات غير الفارغة التي يمكن إضافة 0 إليها بأمان (يا هلا!) ، ولكن لا يجب أن تتسبب في ذلك المؤشر.
  • &x[0]

    • للسلاسل الفارغة هذا له سلوك غير معروف (21.3.4)
      • على سبيل المثال f(const char* p, size_t n) { if (n == 0) return; ...whatever... } f(const char* p, size_t n) { if (n == 0) return; ...whatever... } لا يجب عليك الاتصال بـ f(&x[0], x.size()); عند x.empty() - فقط استخدم f(x.data(), ...) .
    • خلاف ذلك ، حسب x.data() ولكن:
      • لـ const x يؤدي ذلك إلى مؤشر char* غير const ؛ يمكنك الكتابة فوق محتوى السلسلة
  • x.c_str()

    • إرجاع const char* إلى تمثيل ASCIIZ (NUL-terminated) للقيمة (أي ['h' ، 'e' ، 'l' ، 'l' ، 'o' ، '\ 0']).
    • على الرغم من أن القليل من التطبيقات قد اختارت أن تفعل ذلك ، فقد تم صياغة معيار C ++ 03 للسماح بتنفيذ السلسلة بحرية إنشاء مخزن مؤقت منتهي لـ NUL على الطاير ، من المخزن المؤقت الذي من المحتمل ألا يكون NUL "مكشوف" بواسطة x.data() و &x[0]
    • x.size() + 1 أحرف آمنة للقراءة.
    • مضمونة حتى بالنسبة للأوتار الفارغة (['\ 0']).

عواقب الوصول إلى المؤشرات القانونية الخارجية

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

متى يتم إبطال تلك المؤشرات؟

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

راجع أيضًا كيفية الحصول على مؤشر حرف صالح حتى بعد استخدام نطاق x أو تعديله أدناه.

لذلك ، أيهما أفضل للاستخدام؟

من C ++ 11 ، استخدم .c_str() لبيانات .data() و .data() للبيانات "الثنائية" (موضح أكثر أدناه).

في C ++ 03 ، استخدم .c_str() ما لم تكن معينة .data() كافية ، وتفضل .data() على &x[0] حيث أنها آمنة للأوتار الفارغة ....

... حاول أن تفهم البرنامج بما فيه الكفاية لاستخدام data() عند الاقتضاء ، أو ربما ستقوم بأخطاء أخرى ...

يتم استخدام الحرف ASCII NUL '\ 0' الذي تضمنه .c_str() بواسطة العديد من الوظائف كقيمة الحارس تدل على نهاية البيانات ذات الصلة والوصول الآمن. ينطبق هذا على كل من C ++ - وظائف فقط مثل say fstream::fstream(const char* filename, ...) strchr() المشتركة مع C مثل strchr() و printf() .

وبالنظر إلى .c_str() الخاصة بـ C ++ 03's حول المخزن المؤقت الذي تم إرجاعه ، توجد مجموعة فائقة من .data() ، يمكنك دائمًا أن تستخدم بأمان .c_str() ، لكن لا يحدث ذلك أحيانًا بسبب:

  • باستخدام .data() يتصل .data() الآخرين الذين يقرؤون شفرة المصدر بأن البيانات ليست ASCIIZ (بدلاً من ذلك ، أنت تستخدم السلسلة لتخزين مجموعة من البيانات (التي لا تكون في الحقيقة نصية بالفعل)) ، أو أنك " إعادة تمريرها إلى وظيفة أخرى تعاملها ككتلة من البيانات "الثنائية". قد يكون هذا الأمر بمثابة نظرة ثاقبة في ضمان استمرار تغييرات رموز المبرمجين الأخرى في التعامل مع البيانات بشكل صحيح.
  • C ++ 03 فقط: هناك فرصة طفيفة في أن تنفيذ string يجب أن يقوم ببعض تخصيص الذاكرة الإضافية و / أو نسخ البيانات لتحضير المخزن المؤقت المنتهي لـ NUL

x.size() إضافية ، إذا كانت معلمات الدالة تتطلب char* ( const ) char* ولكن لا تصر على الحصول على x.size() ، فربما تحتاج الدالة إلى إدخال ASCIIZ ، لذلك .c_str() هو اختيار جيد (تحتاج الدالة لمعرفة مكان إنهاء النص بطريقة ما ، لذلك إذا لم يكن معلمة منفصلة ، يمكن أن يكون مجرد اتفاقية مثل طول البادئة أو الحارس أو بعض الطول المتوقع الثابت).

كيفية الحصول على مؤشر حرف صالح حتى بعد نطاق x يترك أو يتم تعديله

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

لنسخ النص من std::string x إلى مصفوفة أحرف مستقلة:

// USING ANOTHER STRING - AUTO MEMORY MANAGEMENT, EXCEPTION SAFE
std::string old_x = x;
// - old_x will not be affected by subsequent modifications to x...
// - you can use `&old_x[0]` to get a writable char* to old_x's textual content
// - you can use resize() to reduce/expand the string
//   - resizing isn't possible from within a function passed only the char* address

std::string old_x = x.c_str(); // old_x will terminate early if x embeds NUL
// Copies ASCIIZ data but could be less efficient as it needs to scan memory to
// find the NUL terminator indicating string length before allocating that amount
// of memory to copy into, or more efficient if it ends up allocating/copying a
// lot less content.
// Example, x == "ab\0cd" -> old_x == "ab".

// USING A VECTOR OF CHAR - AUTO, EXCEPTION SAFE, HINTS AT BINARY CONTENT, GUARANTEED CONTIGUOUS EVEN IN C++03
std::vector<char> old_x(x.data(), x.data() + x.size());       // without the NUL
std::vector<char> old_x(x.c_str(), x.c_str() + x.size() + 1);  // with the NUL

// USING STACK WHERE MAXIMUM SIZE OF x IS KNOWN TO BE COMPILE-TIME CONSTANT "N"
// (a bit dangerous, as "known" things are sometimes wrong and often become wrong)
char y[N + 1];
strcpy(y, x.c_str());

// USING STACK WHERE UNEXPECTEDLY LONG x IS TRUNCATED (e.g. Hello\0->Hel\0)
char y[N + 1];
strncpy(y, x.c_str(), N);  // copy at most N, zero-padding if shorter
y[N] = '\0';               // ensure NUL terminated

// USING THE STACK TO HANDLE x OF UNKNOWN (BUT SANE) LENGTH
char* y = alloca(x.size() + 1);
strcpy(y, x.c_str());

// USING THE STACK TO HANDLE x OF UNKNOWN LENGTH (NON-STANDARD GCC EXTENSION)
char y[x.size() + 1];
strcpy(y, x.c_str());

// USING new/delete HEAP MEMORY, MANUAL DEALLOC, NO INHERENT EXCEPTION SAFETY
char* y = new char[x.size() + 1];
strcpy(y, x.c_str());
//     or as a one-liner: char* y = strcpy(new char[x.size() + 1], x.c_str());
// use y...
delete[] y; // make sure no break, return, throw or branching bypasses this

// USING new/delete HEAP MEMORY, SMART POINTER DEALLOCATION, EXCEPTION SAFE
// see boost shared_array usage in Johannes Schaub's answer

// USING malloc/free HEAP MEMORY, MANUAL DEALLOC, NO INHERENT EXCEPTION SAFETY
char* y = strdup(x.c_str());
// use y...
free(y);

هناك أسباب أخرى تود الحصول على char* أو const char* إنشاؤها من string

لذا ، أعلاه رأيت كيف تحصل على char* ( const ) char* ، وكيف تجعل نسخة من النص مستقلة عن string الأصلية ، ولكن ماذا يمكنك أن تفعل بها؟ وهناك القليل من smattering عشوائية ...

  • إعطاء رمز "C" الوصول إلى نص string C ++ ، كما في printf("x is '%s'", x.c_str());
  • نسخ نص x إلى المخزن المؤقت المحدد بواسطة المتصل الخاص بالوظيفة الخاصة بك (مثل strncpy(callers_buffer, callers_buffer_size, x.c_str()) ) ، أو الذاكرة المتطايرة المستخدمة في جهاز الإدخال / الإخراج (على سبيل المثال for (const char* p = x.c_str(); *p; ++p) *p_device = *p; )
  • إلحاق نص x إلى مصفوفة أحرف تحتوي بالفعل على بعض نصوص ASCIIZ (مثل strcat(other_buffer, x.c_str()) ) - يجب الحرص على عدم تجاوز المخزن المؤقت (في كثير من الحالات قد تحتاج إلى استخدام strncat )
  • قم بإرجاع const char* أو char* من دالة (ربما لأسباب تاريخية - استخدام العميل لواجهة برمجة التطبيقات الخاصة بك - أو من أجل توافق C لا تريد إرجاع std::string ، ولكنك ترغب في نسخ بيانات string الخاصة بك في مكان ما للمتصل)
    • يجب الحرص على عدم إرجاع المؤشر الذي قد يتم إلغى الإشارة إليه بواسطة المتصل بعد أن string متغير string المحلي الذي أشار إليه المؤشر في وجود نطاق
    • بعض المشاريع ذات الكائنات المشتركة التي تم تصنيفها / ربطها لتطبيقات std::string مختلفة (مثل STLport و المترجم الأصلي) قد تمر البيانات ASCIIZ لتجنب التعارضات

فقط انظر هذا:

string str1("");
const char * str2 = str1.c_str();

ومع ذلك ، لاحظ أن هذا سيعود const char * . char * ، استخدم strcpy لنسخها إلى صفيف char آخر.


char* result = strcpy((char*)malloc(str.length()+1), str.c_str());




const