سؤال

أنظر أيضا C رمزي


فيما يلي substr() سريع لـ C كتبته (نعم، يجب نقل عمليات تهيئة المتغير لبدء الوظيفة وما إلى ذلك، لكنك حصلت على الفكرة)

لقد رأيت العديد من التطبيقات "الذكية" لـ substr() والتي تعتبر بسيطة وتستدعي strncpy()!

جميعها خاطئة (لا يضمن strncpy الإنهاء الفارغ وبالتالي قد لا ينتج عن الاستدعاء سلسلة فرعية صحيحة!)

هنا شيء ربما أفضل؟

اخراج الخلل!

char* substr(const char* text, int nStartingPos, int nRun)
{
    char* emptyString = strdup(""); /* C'mon! This cannot fail */

    if(text == NULL) return emptyString;

    int textLen = strlen(text);

    --nStartingPos;

    if((nStartingPos < 0) || (nRun <= 0) || (textLen == 0) || (textLen < nStartingPos)) return emptyString;

    char* returnString = (char *)calloc((1 + nRun), sizeof(char));

    if(returnString == NULL) return emptyString;

    strncat(returnString, (nStartingPos + text), nRun);

    /* We do not need emptyString anymore from this point onwards */

    free(emptyString);
    emptyString = NULL;

    return returnString;
}


int main()
{
    const char *text = "-2--4--6-7-8-9-10-11-";

    char *p = substr(text, -1, 2);
    printf("[*]'%s' (\")\n",  ((p == NULL) ? "<NULL>" : p));
    free(p);

    p = substr(text, 1, 2);
    printf("[*]'%s' (-2)\n", ((p == NULL) ? "<NULL>" : p));
    free(p);

    p = substr(text, 3, 2);
    printf("[*]'%s' (--)\n", ((p == NULL) ? "<NULL>" : p));
    free(p);

    p = substr(text, 16, 2);
    printf("[*]'%s' (10)\n", ((p == NULL) ? "<NULL>" : p));
    free(p);

    p = substr(text, 16, 20);
    printf("[*]'%s' (10-11-)\n", ((p == NULL) ? "<NULL>" : p));
    free(p);

    p = substr(text, 100, 2);
    printf("[*]'%s' (\")\n", ((p == NULL) ? "<NULL>" : p));
    free(p);

    p = substr(text, 1, 0);
    printf("[*]'%s' (\")\n", ((p == NULL) ? "<NULL>" : p));
    free(p);

    return 0;
}

انتاج :

[*]'' (")
[*]'-2' (-2)
[*]'--' (--)
[*]'10' (10)
[*]'10-11-' (10-11-)
[*]'' (")
[*]'' (")
هل كانت مفيدة؟

المحلول

أود أن أقول العودة NULL إذا كان الإدخال غير صالح بدلاً من a malloc()إد سلسلة فارغة.بهذه الطريقة يمكنك اختبار ما إذا كانت الوظيفة قد فشلت أم لا if(p) بدلا من if(*p == 0).

أيضًا، أعتقد أن وظيفتك تتسرب من الذاكرة بسبب emptyString فقط free()د في شرط واحد.يجب عليك التأكد من ذلك free() ذلك دون قيد أو شرط، أي.مباشرة قبل return.

أما بالنسبة لتعليقك على strncpy() ليس NUL-إنهاء السلسلة (وهذا صحيح)، إذا كنت تستخدم calloc() لتخصيص السلسلة بدلاً من malloc(), ، لن تكون هذه مشكلة إذا قمت بتخصيص بايت واحد أكثر مما قمت بنسخه، منذ ذلك الحين calloc() يقوم تلقائيًا بتعيين كافة القيم (بما في ذلك، في هذه الحالة، النهاية) على 0.

سأعطيك المزيد من الملاحظات ولكني أكره قراءة كود CamelCase.ليس أن هناك أي شيء خاطئ في ذلك.

يحرر:بالنسبه لتحديثاتك :

انتبه إلى أن معيار C يحدد sizeof(char) لتكون 1 بغض النظر عن النظام الخاص بك.إذا كنت تستخدم جهاز كمبيوتر يستخدم 9 بت في البايت (لا سمح الله)، sizeof(char) لا يزال سيكون 1.لا يعني ذلك أن هناك أي خطأ في القول sizeof(char) - يُظهر نيتك بوضوح ويوفر التناسق مع المكالمات calloc() أو malloc() لأنواع أخرى.لكن sizeof(int) مفيد فعلا (ints يمكن أن تكون بأحجام مختلفة على أجهزة الكمبيوتر 16 و32 وأجهزة الكمبيوتر الجديدة ذات 64 بت).كلما عرفت أكثر.

أود أيضًا أن أكرر أن الاتساق مع معظم أكواد C الأخرى يجب أن يعود NULL على خطأ بدلا من "".أعرف العديد من الوظائف (مثل strcmp()) من المحتمل أن يفعل أشياء سيئة إذا قمت بتمريرها NULL - وهذا أمر متوقع.لكن مكتبة C القياسية (والعديد من واجهات برمجة تطبيقات C الأخرى) تتبع نهج "يقع على عاتق المتصل مسؤولية التحقق من وجود NULL, ، وليست مسؤولية الوظيفة تجاه الطفل إذا لم يفعل ذلك." إذا كنت تريد القيام بذلك بالطريقة الأخرى، فهذا رائع، ولكنه يتعارض مع أحد أقوى الاتجاهات في تصميم واجهة C.

أيضا، أود أن تستخدم strncpy() (أو memcpy()) بدلا من strncat().استخدام strncat()strcat()) يحجب نيتك - فهو يجعل الشخص الذي ينظر إلى الكود الخاص بك يعتقد أنك تريد إضافته إلى نهاية السلسلة (وهو ما تفعله، لأنه بعد ذلك calloc(), ، النهاية هي البداية)، عندما يكون ما تريد القيام به هو ضبط السلسلة. strncat() يجعلها تبدو وكأنك تضيف إلى سلسلة، بينما strcpy() (أو روتين نسخ آخر) من شأنه أن يجعلها تبدو أقرب إلى نيتك.الأسطر الثلاثة التالية كلها تفعل الشيء نفسه في هذا السياق - اختر أيهما تعتقد أنه يبدو أجمل:

strncat(returnString, text + nStartingPos, nRun);

strncpy(returnString, text + nStartingPos, nRun);

memcpy(returnString, text + nStartingPos, nRun);

زائد، strncpy() و memcpy() من المحتمل أن يكون (قليلاً) أسرع/أكثر كفاءة من strncat().

text + nStartingPos بالضبط مثل nStartingPos + text - سأضع char * أولاً، أعتقد أن هذا أكثر وضوحًا، ولكن أيًا كان الترتيب الذي تريد وضعه فيه فهو متروك لك.كما أن الأقواس المحيطة بها غير ضرورية (لكنها لطيفة)، منذ ذلك الحين + له أسبقية أعلى من ,.

تحرير 2:الأسطر الثلاثة من التعليمات البرمجية لا تفعل نفس الشيء، ولكن في هذا السياق ستنتج جميعها نفس النتيجة.شكرا لاصطياد لي على ذلك.

نصائح أخرى

تبدو وظيفتك معقدة للغاية بالنسبة لما ينبغي أن يكون عملية بسيطة.بعض المشاكل (ليست جميعها أخطاء):

  • strdup(), ، وغيرها من وظائف تخصيص الذاكرة، يستطيع تفشل، يجب أن تسمح لجميع المشاكل المحتملة.
  • قم بتخصيص الموارد (الذاكرة في هذه الحالة) فقط إذا كنت في حاجة إليها ومتى.
  • يجب أن تكون قادرًا على التمييز بين الأخطاء واللدغات الصحيحة.في هذه اللحظة، أنت لا تعرف ما إذا كان malloc() فشل في substr ("xxx",1,1) أو عمل substr ("xxx",1,0) ينتج سلسلة فارغة.
  • لا تحتاج إلى ذلك calloc() الذاكرة التي ستقوم بالكتابة فوقها على أي حال.
  • يجب أن تتسبب جميع المعلمات غير الصالحة في حدوث خطأ أو يتم إجبارها على معلمة صالحة (ويجب أن توثق واجهة برمجة التطبيقات الخاصة بك ذلك).
  • لا تحتاج إلى تعيين السلسلة الفارغة المحلية على NULL بعد تحريرها - سيتم فقدها عند إرجاع الوظيفة.
  • لا تحتاج إلى usr strncat() - أنت يجب تعرف على الأحجام والذاكرة المتوفرة لديك قبل القيام بأي عملية نسخ حتى تتمكن من استخدام (على الأغلب) بشكل أسرع memcpy().
  • أنت تستخدم القاعدة 1 بدلاً من القاعدة 0 لإزاحات السلسلة التي تتعارض مع اتجاه C.

المقطع التالي هو ما سأفعله (أفضل أن يتم حساب لغة بايثون للقيم السالبة من نهاية السلسلة ولكني احتفظت بالطول بدلاً من موضع النهاية).

char *substr (const char *inpStr, int startPos, int strLen) {
    /* Cannot do anything with NULL. */

    if (inpStr == NULL) return NULL;

    /* All negative positions to go from end, and cannot
       start before start of string, force to start. */

    if (startPos < 0)
        startPos = strlen (inpStr) + startPos;
    if (startPos < 0)
        startPos = 0;

    /* Force negative lengths to zero and cannot
       start after end of string, force to end. */

    if (strLen < 0)
        strLen = 0;
    if (startPos >strlen (inpStr))
        startPos = strlen (inpStr);

    /* Adjust length if source string too short. */

    if (strLen > strlen (&inpStr[startPos]))
        strLen = strlen (&inpStr[startPos]);

    /* Get long enough string from heap, return NULL if no go. */

    if ((buff = malloc (strLen + 1)) == NULL)
        return NULL;

    /* Transfer string section and return it. */

    memcpy (buff, &(inpStr[startPos]), strLen);
    buff[strLen] = '\0';

    return buff;
}
char* emptyString = strdup(""); /* C'mon! This cannot fail? */

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

قد يفشل strdup (على الرغم من أنه من غير المحتمل جدًا ولا يستحق التحقق منه، IMHO).ولكن لديها مشكلة أخرى - فهي ليست دالة قياسية C.سيكون من الأفضل استخدام malloc.

يمكنك أيضًا استخدام الدالة memmove لإرجاع سلسلة فرعية من البداية إلى الطول.تحسين/إضافة حل آخر من حل paxdiablo:

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>

    char *splitstr(char *idata, int start, int slen) {
            char ret[150];
            if(slen == NULL) {
                    slen=strlen(idata)-start;
            }
            memmove (ret,idata+start,slen);
            return ret;
    }

    /*
    Usage:
            char ostr[]="Hello World!";
            char *ores=splitstr(ostr, 0, 5);
            Outputs:
                    Hello
    */

نأمل أن يساعد.تم اختباره على نظام التشغيل Windows 7 Home Premium باستخدام مترجم TCC C.

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