كيف أقوم بقص المسافات البيضاء البادئة/اللاحقة بطريقة قياسية؟

StackOverflow https://stackoverflow.com/questions/122616

  •  02-07-2019
  •  | 
  •  

سؤال

هل هناك طريقة نظيفة ويفضل أن تكون قياسية لقص المسافات البيضاء البادئة والزائدة من سلسلة في لغة C؟سأطرح مشكلتي الخاصة، لكنني أعتقد أن هذه مشكلة شائعة ولها حل مشترك بنفس القدر.

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

المحلول

إذا كان بإمكانك تعديل السلسلة:

// Note: This function returns a pointer to a substring of the original string.
// If the given string was allocated dynamically, the caller must not overwrite
// that pointer with the returned value, since the original pointer must be
// deallocated using the same allocator with which it was allocated.  The return
// value must NOT be deallocated using free() etc.
char *trimwhitespace(char *str)
{
  char *end;

  // Trim leading space
  while(isspace((unsigned char)*str)) str++;

  if(*str == 0)  // All spaces?
    return str;

  // Trim trailing space
  end = str + strlen(str) - 1;
  while(end > str && isspace((unsigned char)*end)) end--;

  // Write new null terminator character
  end[1] = '\0';

  return str;
}

إذا لم تتمكن من تعديل السلسلة، فيمكنك استخدام نفس الطريقة بشكل أساسي:

// Stores the trimmed input string into the given output buffer, which must be
// large enough to store the result.  If it is too small, the output is
// truncated.
size_t trimwhitespace(char *out, size_t len, const char *str)
{
  if(len == 0)
    return 0;

  const char *end;
  size_t out_size;

  // Trim leading space
  while(isspace((unsigned char)*str)) str++;

  if(*str == 0)  // All spaces?
  {
    *out = 0;
    return 1;
  }

  // Trim trailing space
  end = str + strlen(str) - 1;
  while(end > str && isspace((unsigned char)*end)) end--;
  end++;

  // Set output size to minimum of trimmed string length and buffer size minus 1
  out_size = (end - str) < len-1 ? (end - str) : len-1;

  // Copy trimmed string and add null terminator
  memcpy(out, str, out_size);
  out[out_size] = 0;

  return out_size;
}

نصائح أخرى

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

char *trim(char *str)
{
    size_t len = 0;
    char *frontp = str;
    char *endp = NULL;

    if( str == NULL ) { return NULL; }
    if( str[0] == '\0' ) { return str; }

    len = strlen(str);
    endp = str + len;

    /* Move the front and back pointers to address the first non-whitespace
     * characters from each end.
     */
    while( isspace((unsigned char) *frontp) ) { ++frontp; }
    if( endp != frontp )
    {
        while( isspace((unsigned char) *(--endp)) && endp != frontp ) {}
    }

    if( str + len - 1 != endp )
            *(endp + 1) = '\0';
    else if( frontp != str &&  endp == frontp )
            *str = '\0';

    /* Shift the string so that it starts at str so that if it's dynamically
     * allocated, we can still free it on the returned pointer.  Note the reuse
     * of endp to mean the front of the string buffer now.
     */
    endp = str;
    if( frontp != str )
    {
            while( *frontp ) { *endp++ = *frontp++; }
            *endp = '\0';
    }


    return str;
}

اختبار الصحة:

int main(int argc, char *argv[])
{
    char *sample_strings[] =
    {
            "nothing to trim",
            "    trim the front",
            "trim the back     ",
            " trim one char front and back ",
            " trim one char front",
            "trim one char back ",
            "                   ",
            " ",
            "a",
            "",
            NULL
    };
    char test_buffer[64];
    int index;

    for( index = 0; sample_strings[index] != NULL; ++index )
    {
            strcpy( test_buffer, sample_strings[index] );
            printf("[%s] -> [%s]\n", sample_strings[index],
                                     trim(test_buffer));
    }

    /* The test prints the following:
    [nothing to trim] -> [nothing to trim]
    [    trim the front] -> [trim the front]
    [trim the back     ] -> [trim the back]
    [ trim one char front and back ] -> [trim one char front and back]
    [ trim one char front] -> [trim one char front]
    [trim one char back ] -> [trim one char back]
    [                   ] -> []
    [ ] -> []
    [a] -> [a]
    [] -> []
    */

    return 0;
}

الملف المصدر كان Trim.c.تم تجميعها باستخدام "cc Trim.c -o Trim".

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

void trim(char * s) {
    char * p = s;
    int l = strlen(p);

    while(isspace(p[l - 1])) p[--l] = 0;
    while(* p && isspace(* p)) ++p, --l;

    memmove(s, p, l + 1);
}   

يقوم هذا الإصدار بإنشاء نسخة من السلسلة باستخدام strndup() بدلاً من تحريرها في مكانها.تتطلب الدالة strndup() _GNU_SOURCE، لذا ربما تحتاج إلى إنشاء الدالة strndup() الخاصة بك باستخدام malloc() وstrncpy().

char * trim(char * s) {
    int l = strlen(s);

    while(isspace(s[l - 1])) --l;
    while(* s && isspace(* s)) ++s, --l;

    return strndup(s, l);
}

ها هي مكتبة C mini الخاصة بي لقص اليسار واليمين وكلاهما وكل شيء في مكانه ومنفصل، وقص مجموعة من الأحرف المحددة (أو المساحة البيضاء افتراضيًا).

محتويات strlib.h:

#ifndef STRLIB_H_
#define STRLIB_H_ 1
enum strtrim_mode_t {
    STRLIB_MODE_ALL       = 0, 
    STRLIB_MODE_RIGHT     = 0x01, 
    STRLIB_MODE_LEFT      = 0x02, 
    STRLIB_MODE_BOTH      = 0x03
};

char *strcpytrim(char *d, // destination
                 char *s, // source
                 int mode,
                 char *delim
                 );

char *strtriml(char *d, char *s);
char *strtrimr(char *d, char *s);
char *strtrim(char *d, char *s); 
char *strkill(char *d, char *s);

char *triml(char *s);
char *trimr(char *s);
char *trim(char *s);
char *kill(char *s);
#endif

محتويات strlib.c:

#include <strlib.h>

char *strcpytrim(char *d, // destination
                 char *s, // source
                 int mode,
                 char *delim
                 ) {
    char *o = d; // save orig
    char *e = 0; // end space ptr.
    char dtab[256] = {0};
    if (!s || !d) return 0;

    if (!delim) delim = " \t\n\f";
    while (*delim) 
        dtab[*delim++] = 1;

    while ( (*d = *s++) != 0 ) { 
        if (!dtab[0xFF & (unsigned int)*d]) { // Not a match char
            e = 0;       // Reset end pointer
        } else {
            if (!e) e = d;  // Found first match.

            if ( mode == STRLIB_MODE_ALL || ((mode != STRLIB_MODE_RIGHT) && (d == o)) ) 
                continue;
        }
        d++;
    }
    if (mode != STRLIB_MODE_LEFT && e) { // for everything but trim_left, delete trailing matches.
        *e = 0;
    }
    return o;
}

// perhaps these could be inlined in strlib.h
char *strtriml(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_LEFT, 0); }
char *strtrimr(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_RIGHT, 0); }
char *strtrim(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_BOTH, 0); }
char *strkill(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_ALL, 0); }

char *triml(char *s) { return strcpytrim(s, s, STRLIB_MODE_LEFT, 0); }
char *trimr(char *s) { return strcpytrim(s, s, STRLIB_MODE_RIGHT, 0); }
char *trim(char *s) { return strcpytrim(s, s, STRLIB_MODE_BOTH, 0); }
char *kill(char *s) { return strcpytrim(s, s, STRLIB_MODE_ALL, 0); }

الروتين الرئيسي الوحيد يفعل كل شيء.فإنه الديكورات في مكان إذا src == التوقيت الصيفي, ، وإلا ، فهو يعمل مثل strcpy إجراءات.يقوم بقص مجموعة من الأحرف المحددة في السلسلة delim, أو مسافة بيضاء إذا كانت فارغة.يقوم بقص اليسار واليمين وكلاهما وكل شيء (مثل tr).ليس هناك الكثير منها، وهي تتكرر عبر السلسلة مرة واحدة فقط.قد يشتكي بعض الأشخاص من أن القطع من اليمين يبدأ من اليسار، ومع ذلك، ليست هناك حاجة إلى السترلين الذي يبدأ من اليسار على أي حال.(بطريقة أو بأخرى، يتعين عليك الوصول إلى نهاية السلسلة للحصول على القطع الصحيحة، لذلك يمكنك أيضًا القيام بالعمل أثناء تقدمك.) قد تكون هناك حجج يجب تقديمها حول خطوط الأنابيب وأحجام ذاكرة التخزين المؤقت وما إلى ذلك - من يدري .وبما أن الحل يعمل من اليسار إلى اليمين ويتكرر مرة واحدة فقط، فيمكن توسيعه ليعمل على التدفقات أيضًا.محددات:نعم هو كذلك لا يعمل على يونيكود سلاسل.

هذه هي محاولتي لوظيفة القطع البسيطة والصحيحة في مكانها.

void trim(char *str)
{
    int i;
    int begin = 0;
    int end = strlen(str) - 1;

    while (isspace((unsigned char) str[begin]))
        begin++;

    while ((end >= begin) && isspace((unsigned char) str[end]))
        end--;

    // Shift all characters back to the start of the string array.
    for (i = begin; i <= end; i++)
        str[i - begin] = str[i];

    str[i - begin] = '\0'; // Null terminate string.
}

في وقت متأخر للحزب تقليم

سمات:
1.قم بقص البداية بسرعة، كما هو الحال في عدد من الإجابات الأخرى.
2.بعد الوصول إلى النهاية، قم بقص الجانب الأيمن باختبار واحد فقط لكل حلقة.مثل @jfm3، ولكنه يعمل مع سلسلة ذات مسافة بيضاء بالكامل)
3.لتجنب السلوك غير المحدد عندما char تم التوقيع char, ، يقذف *s ل unsigned char.

التعامل مع الشخصيات "في جميع الأحوال الحجة هي int, ، والتي يجب تمثيل قيمتها على أنها unsigned char أو يجب أن تساوي قيمة الماكرو EOF.إذا كانت الوسيطة لها أي قيمة أخرى، فسيكون السلوك غير محدد." C11 §7.4 1

#include <ctype.h>

// Return a pointer to the trimmed string
char *string_trim_inplace(char *s) {
  while (isspace((unsigned char) *s)) s++;
  if (*s) {
    char *p = s;
    while (*p) p++;
    while (isspace((unsigned char) *(--p)));
    p[1] = '\0';
  }

  // If desire to shift the trimmed string

  return s;
}

@chqrlie علق أعلاه لا يغير السلسلة المشذبة.لنفعل ذلك....

// Return a pointer to the (shifted) trimmed string
char *string_trim_inplace(char *s) {
  char *original = s;
  size_t len = 0;

  while (isspace((unsigned char) *s)) {
    s++;
  } 
  if (*s) {
    char *p = s;
    while (*p) p++;
    while (isspace((unsigned char) *(--p)));
    p[1] = '\0';
    len = (size_t) (p - s);
  }

  return (s == original) ? s : memove(original, s, len + 1);
}

إليك حلًا مشابهًا لروتين التعديل الموضعي @adam-rosenfields ولكن دون اللجوء إلى strlen() دون داع.مثل @jkramer، يتم ضبط السلسلة إلى اليسار داخل المخزن المؤقت حتى تتمكن من تحرير نفس المؤشر.ليس مثاليًا للسلاسل الكبيرة لأنه لا يستخدم memmove.يتضمن عوامل التشغيل ++/-- التي يذكرها @jfm3. فكتكسوشملت اختبارات الوحدة القائمة.

#include <ctype.h>

void trim(char * const a)
{
    char *p = a, *q = a;
    while (isspace(*q))            ++q;
    while (*q)                     *p++ = *q++;
    *p = '\0';
    while (p > a && isspace(*--p)) *p = '\0';
}

/* See http://fctx.wildbearsoftware.com/ */
#include "fct.h"

FCT_BGN()
{
    FCT_QTEST_BGN(trim)
    {
        { char s[] = "";      trim(s); fct_chk_eq_str("",    s); } // Trivial
        { char s[] = "   ";   trim(s); fct_chk_eq_str("",    s); } // Trivial
        { char s[] = "\t";    trim(s); fct_chk_eq_str("",    s); } // Trivial
        { char s[] = "a";     trim(s); fct_chk_eq_str("a",   s); } // NOP
        { char s[] = "abc";   trim(s); fct_chk_eq_str("abc", s); } // NOP
        { char s[] = "  a";   trim(s); fct_chk_eq_str("a",   s); } // Leading
        { char s[] = "  a c"; trim(s); fct_chk_eq_str("a c", s); } // Leading
        { char s[] = "a  ";   trim(s); fct_chk_eq_str("a",   s); } // Trailing
        { char s[] = "a c  "; trim(s); fct_chk_eq_str("a c", s); } // Trailing
        { char s[] = " a ";   trim(s); fct_chk_eq_str("a",   s); } // Both
        { char s[] = " a c "; trim(s); fct_chk_eq_str("a c", s); } // Both

        // Villemoes pointed out an edge case that corrupted memory.  Thank you.
        // http://stackoverflow.com/questions/122616/#comment23332594_4505533
        {
          char s[] = "a     ";       // Buffer with whitespace before s + 2
          trim(s + 2);               // Trim "    " containing only whitespace
          fct_chk_eq_str("", s + 2); // Ensure correct result from the trim
          fct_chk_eq_str("a ", s);   // Ensure preceding buffer not mutated
        }

        // doukremt suggested I investigate this test case but
        // did not indicate the specific behavior that was objectionable.
        // http://stackoverflow.com/posts/comments/33571430
        {
          char s[] = "         foobar";  // Shifted across whitespace
          trim(s);                       // Trim
          fct_chk_eq_str("foobar", s);   // Leading string is correct

          // Here is what the algorithm produces:
          char r[16] = { 'f', 'o', 'o', 'b', 'a', 'r', '\0', ' ',                     
                         ' ', 'f', 'o', 'o', 'b', 'a', 'r', '\0'};
          fct_chk_eq_int(0, memcmp(s, r, sizeof(s)));
        }
    }
    FCT_QTEST_END();
}
FCT_END();

واحد آخر، مع سطر واحد يقوم بالمهمة الحقيقية:

#include <stdio.h>

int main()
{
   const char *target = "   haha   ";
   char buf[256];
   sscanf(target, "%s", buf); // Trimming on both sides occurs here
   printf("<%s>\n", buf);
}

لم تعجبني معظم هذه الإجابات لأنها قامت بواحد أو أكثر من الإجراءات التالية ...

  1. أعاد مؤشرًا مختلفًا داخل سلسلة المؤشر الأصلي (نوع من الألم للتوفيق بين مؤشرين مختلفين لنفس الشيء).
  2. الاستفادة المجانية من أشياء مثل سترلين () التي تقوم بتكرار السلسلة بأكملها مسبقًا.
  3. تم استخدام وظائف lib غير المحمولة الخاصة بنظام التشغيل.
  4. تم المسح العكسي.
  5. تستخدم المقارنة ل ' ' بدلاً من إيسباس () بحيث يتم الحفاظ على TAB/CR/LF.
  6. الذاكرة الضائعة مع المخازن المؤقتة الثابتة الكبيرة.
  7. دورات ضائعة مع وظائف عالية التكلفة مثل sscanf/sprintf.

هنا هو الإصدار الخاص بي:

void fnStrTrimInPlace(char *szWrite) {

    const char *szWriteOrig = szWrite;
    char       *szLastSpace = szWrite, *szRead = szWrite;
    int        bNotSpace;

    // SHIFT STRING, STARTING AT FIRST NON-SPACE CHAR, LEFTMOST
    while( *szRead != '\0' ) {

        bNotSpace = !isspace((unsigned char)(*szRead));

        if( (szWrite != szWriteOrig) || bNotSpace ) {

            *szWrite = *szRead;
            szWrite++;

            // TRACK POINTER TO LAST NON-SPACE
            if( bNotSpace )
                szLastSpace = szWrite;
        }

        szRead++;
    }

    // TERMINATE AFTER LAST NON-SPACE (OR BEGINNING IF THERE WAS NO NON-SPACE)
    *szLastSpace = '\0';
}

تأخرت كثيرا عن الحفلة...

حل المسح الأمامي بتمريرة واحدة دون أي تراجع.يتم اختبار كل حرف في السلسلة المصدر بدقة مرة واحدة مرتين.(لذلك يجب أن يكون أسرع من معظم الحلول الأخرى هنا، خاصة إذا كانت السلسلة المصدر تحتوي على الكثير من المسافات الزائدة.)

يتضمن ذلك حلين، أحدهما لنسخ سلسلة مصدر وقصها إلى سلسلة وجهة أخرى، والآخر لقص السلسلة المصدر في مكانها.كلتا الوظيفتين تستخدمان نفس الرمز.

يتم نقل السلسلة (القابلة للتعديل) إلى مكانها، بحيث يظل المؤشر الأصلي لها دون تغيير.

#include <stddef.h>
#include <ctype.h>

char * trim2(char *d, const char *s)
{
    // Sanity checks
    if (s == NULL  ||  d == NULL)
        return NULL;

    // Skip leading spaces        
    const unsigned char * p = (const unsigned char *)s;
    while (isspace(*p))
        p++;

    // Copy the string
    unsigned char * dst = (unsigned char *)d;   // d and s can be the same
    unsigned char * end = dst;
    while (*p != '\0')
    {
        if (!isspace(*dst++ = *p++))
            end = dst;
    }

    // Truncate trailing spaces
    *end = '\0';
    return d;
}

char * trim(char *s)
{
    return trim2(s, s);
}

لست متأكدًا مما تعتبره "غير مؤلم".

سلاسل C مؤلمة جدًا.يمكننا العثور على موضع الحرف الأول الذي لا يحتوي على مسافة بيضاء بشكل تافه:

while (isspace(* p)) p++;

يمكننا العثور على آخر موضع للأحرف غير المسافة البيضاء بحركتين تافهتين متشابهتين:

while (* q) q++;
do { q--; } while (isspace(* q));

(لقد وفرت عليك ألم استخدام * و ++ المشغلين في نفس الوقت.)

والسؤال الآن هو ماذا تفعل بهذا؟نوع البيانات الموجود ليس في الواقع ملخصًا قويًا كبيرًا String من السهل التفكير في ذلك، ولكن بدلاً من ذلك لا يزيد عن مجرد مجموعة من بايتات التخزين.نظرًا لعدم وجود نوع بيانات قوي، فمن المستحيل كتابة دالة يمكنها القيام بنفس وظيفة PHperytonby. chomp وظيفة.ما الذي ستعود به هذه الوظيفة في لغة C؟

إستخدم مكتبة السلسلة, ، على سبيل المثال:

Ustr *s1 = USTR1(\7, " 12345 ");

ustr_sc_trim_cstr(&s1, " ");
assert(ustr_cmp_cstr_eq(s1, "12345"));

... كما قلت، هذه مشكلة "شائعة"، نعم تحتاج إلى تضمين #include أو نحو ذلك وهي غير مضمنة في libc ولكن لا تخترع وظيفة الاختراق الخاصة بك لتخزين المؤشرات العشوائية وsize_t بهذه الطريقة تؤدي فقط إلى تجاوزات المخزن المؤقت.

#include "stdafx.h"
#include "malloc.h"
#include "string.h"

int main(int argc, char* argv[])
{

  char *ptr = (char*)malloc(sizeof(char)*30);
  strcpy(ptr,"            Hel  lo    wo           rl   d G    eo rocks!!!    by shahil    sucks b i          g       tim           e");

  int i = 0, j = 0;

  while(ptr[j]!='\0')
  {

      if(ptr[j] == ' ' )
      {
          j++;
          ptr[i] = ptr[j];
      }
      else
      {
          i++;
          j++;
          ptr[i] = ptr[j];
      }
  }


  printf("\noutput-%s\n",ptr);
        return 0;
}

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

int _tmain(int argc, _TCHAR* argv[])
{
   FILE * fp;   // test file
   char currDBSStatstr[100] = {"/0"};
   char *beg;
   char *end;
   char *str1;
   char str[] = "Initializing DBS Configuration";
   fp = fopen("file2-1.txt","r");
   if (fp != NULL)
   {
      printf("File exists.\n");
      fgets(currDBSStatstr, sizeof(currDBSStatstr), fp);
   }
   else
   {
      printf("Error.\n");
      exit(2);
   }  
   //print string
   printf("String: %s\n", currDBSStatstr);
   //extract first string
   str1 = strtok(currDBSStatstr, ":-");
   //print first token
   printf("%s\n", str1);
   //get more tokens in sequence
   while(1)
   {
      //extract more tokens in sequence
      str1 = strtok(NULL, ":-");
      //check to see if done
      if (str1 == NULL)
      {
         printf("Tokenizing Done.\n");
         exit(0);
      }
      //print string after tokenizing Done
      printf("%s\n", str1);
      end = str1 + strlen(str1) - 1;
      while((end > str1) && (*end == '\n'))
      {
         end--;
         *(end+1) = 0;
         beg = str1;
         while(isspace(*str1))
            str1++;
      }
      printf("%s\n", str1);
      if (strcmp(str, str1) == 0)
         printf("Strings are equal.\n");
   }
   return 0;

}

انتاج |

الملف موجود.

خيط:حالة دي بي إس:بدء تشغيل DBS - تهيئة تكوين DBS

ولاية دي بي اس

بدء تشغيل دي بي إس

بدء تشغيل دي بي إس

تهيئة تكوين DBS

تهيئة تكوين DBS

السلاسل متساوية.

تم الترميز.

إذا كنت تستخدم glib, ، ثم يمكنك استخدام g_strstrip

فقط للحفاظ على هذا النمو، هناك خيار آخر بسلسلة قابلة للتعديل:

void trimString(char *string)
{
    size_t i = 0, j = strlen(string);
    while (j > 0 && isspace((unsigned char)string[j - 1])) string[--j] = '\0';
    while (isspace((unsigned char)string[i])) i++;
    if (i > 0) memmove(string, string + i, j - i + 1);
}

أعلم أن هناك العديد من الإجابات، لكنني أنشر إجابتي هنا لمعرفة ما إذا كان الحل جيدًا بما يكفي.

// Trims leading whitespace chars in left `str`, then copy at almost `n - 1` chars
// into the `out` buffer in which copying might stop when the first '\0' occurs, 
// and finally append '\0' to the position of the last non-trailing whitespace char.
// Reture the length the trimed string which '\0' is not count in like strlen().
size_t trim(char *out, size_t n, const char *str)
{
    // do nothing
    if(n == 0) return 0;    

    // ptr stop at the first non-leading space char
    while(isspace(*str)) str++;    

    if(*str == '\0') {
        out[0] = '\0';
        return 0;
    }    

    size_t i = 0;    

    // copy char to out until '\0' or i == n - 1
    for(i = 0; i < n - 1 && *str != '\0'; i++){
        out[i] = *str++;
    }    

    // deal with the trailing space
    while(isspace(out[--i]));    

    out[++i] = '\0';
    return i;
}

أسهل طريقة لتخطي المسافات البادئة في سلسلة هي، imho،

#include <stdio.h>

int main()
{
char *foo="     teststring      ";
char *bar;
sscanf(foo,"%s",bar);
printf("String is >%s<\n",bar);
    return 0;
}

طيب هذا هو رأيي في السؤال.أعتقد أن هذا هو الحل الأكثر إيجازًا الذي يعدل السلسلة الموجودة في مكانها (free سوف تعمل) ويتجنب أي UB.بالنسبة للسلاسل الصغيرة، ربما يكون أسرع من الحل الذي يتضمن memmove.

void stripWS_LT(char *str)
{
    char *a = str, *b = str;
    while (isspace((unsigned char)*a)) a++;
    while (*b = *a++)  b++;
    while (b > str && isspace((unsigned char)*--b)) *b = 0;
}
#include <ctype.h>
#include <string.h>

char *trim_space(char *in)
{
    char *out = NULL;
    int len;
    if (in) {
        len = strlen(in);
        while(len && isspace(in[len - 1])) --len;
        while(len && *in && isspace(*in)) ++in, --len;
        if (len) {
            out = strndup(in, len);
        }
    }
    return out;
}

isspace يساعد على تقليم كافة المساحات البيضاء.

  • قم بتشغيل الحلقة الأولى للتحقق من البايت الأخير لحرف المسافة وتقليل متغير الطول
  • قم بتشغيل حلقة ثانية للتحقق من البايت الأول لحرف المسافة وتقليل متغير الطول وزيادة مؤشر الحرف.
  • وأخيرًا، إذا كان متغير الطول أكبر من 0، فاستخدمه strndup لإنشاء مخزن مؤقت جديد للسلسلة عن طريق استبعاد المسافات.

أنا شخصياً سأطرح نفسي.يمكنك استخدام strtok، ولكن عليك توخي الحذر عند القيام بذلك (خاصة إذا كنت تقوم بإزالة الأحرف البادئة) حتى تعرف ما هي الذاكرة.

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

لقد تأخرت قليلاً عن المباراة، لكنني سأضع روتيني في المعركة.من المحتمل أنهم ليسوا الأكثر كفاءة على الإطلاق، لكنني أعتقد أنهم على صواب وأنهم بسيطون (مع rtrim() دفع مظروف التعقيد):

#include <ctype.h>
#include <string.h>

/*
    Public domain implementations of in-place string trim functions

    Michael Burr
    michael.burr@nth-element.com
    2010
*/

char* ltrim(char* s) 
{
    char* newstart = s;

    while (isspace( *newstart)) {
        ++newstart;
    }

    // newstart points to first non-whitespace char (which might be '\0')
    memmove( s, newstart, strlen( newstart) + 1); // don't forget to move the '\0' terminator

    return s;
}


char* rtrim( char* s)
{
    char* end = s + strlen( s);

    // find the last non-whitespace character
    while ((end != s) && isspace( *(end-1))) {
            --end;
    }

    // at this point either (end == s) and s is either empty or all whitespace
    //      so it needs to be made empty, or
    //      end points just past the last non-whitespace character (it might point
    //      at the '\0' terminator, in which case there's no problem writing
    //      another there).    
    *end = '\0';

    return s;
}

char*  trim( char* s)
{
    return rtrim( ltrim( s));
}

معظم الإجابات حتى الآن تقوم بأحد الإجراءات التالية:

  1. التراجع في نهاية السلسلة (أيابحث عن نهاية السلسلة ثم ابحث للخلف حتى يتم العثور على حرف غير مسافات،) أو
  2. يتصل strlen() أولا، إجراء تمريرة ثانية عبر السلسلة بأكملها.

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

static char const WHITESPACE[] = " \t\n\r";

static void get_trim_bounds(char  const *s,
                            char const **firstWord,
                            char const **trailingSpace)
{
    char const *lastWord;
    *firstWord = lastWord = s + strspn(s, WHITESPACE);
    do
    {
        *trailingSpace = lastWord + strcspn(lastWord, WHITESPACE);
        lastWord = *trailingSpace + strspn(*trailingSpace, WHITESPACE);
    }
    while (*lastWord != '\0');
}

char *copy_trim(char const *s)
{
    char const *firstWord, *trailingSpace;
    char *result;
    size_t newLength;

    get_trim_bounds(s, &firstWord, &trailingSpace);
    newLength = trailingSpace - firstWord;

    result = malloc(newLength + 1);
    memcpy(result, firstWord, newLength);
    result[newLength] = '\0';
    return result;
}

void inplace_trim(char *s)
{
    char const *firstWord, *trailingSpace;
    size_t newLength;

    get_trim_bounds(s, &firstWord, &trailingSpace);
    newLength = trailingSpace - firstWord;

    memmove(s, firstWord, newLength);
    s[newLength] = '\0';
}

هذا هو أقصر تنفيذ ممكن يمكنني التفكير فيه:

static const char *WhiteSpace=" \n\r\t";
char* trim(char *t)
{
    char *e=t+(t!=NULL?strlen(t):0);               // *e initially points to end of string
    if (t==NULL) return;
    do --e; while (strchr(WhiteSpace, *e) && e>=t);  // Find last char that is not \r\n\t
    *(++e)=0;                                      // Null-terminate
    e=t+strspn (t,WhiteSpace);                           // Find first char that is not \t
    return e>t?memmove(t,e,strlen(e)+1):t;                  // memmove string contents and terminator
}

ستعمل هذه الوظائف على تعديل المخزن المؤقت الأصلي ، لذلك إذا تم تخصيصه ديناميكيًا ، فيمكن تحرير المؤشر الأصلي.

#include <string.h>

void rstrip(char *string)
{
  int l;
  if (!string)
    return;
  l = strlen(string) - 1;
  while (isspace(string[l]) && l >= 0)
    string[l--] = 0;
}

void lstrip(char *string)
{
  int i, l;
  if (!string)
    return;
  l = strlen(string);
  while (isspace(string[(i = 0)]))
    while(i++ < l)
      string[i-1] = string[i];
}

void strip(char *string)
{
  lstrip(string);
  rstrip(string);
}

ما رأيك في استخدام وظيفة StrTrim المحددة في الرأس Shlwapi.h.؟إنه أمر مستقيم للأمام بدلاً من تحديده بنفسك.
التفاصيل يمكن العثور عليها على:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773454(v=vs.85).aspx

اذا كنت تمتلك
char ausCaptain[]="GeorgeBailey ";
StrTrim(ausCaptain," ");
هذا سوف يعطي ausCaptain مثل "GeorgeBailey" لا "GeorgeBailey ".

لتقليص خيوطاتي من الجانبين ، أستخدم القديم ولكن goody ؛) يمكن أن تقطع أي شيء مع ASCII أقل من مساحة ، مما يعني أن شروط التحكم سيتم قطعها أيضًا!

char *trimAll(char *strData)
{
  unsigned int L = strlen(strData);
  if(L > 0){ L--; }else{ return strData; }
  size_t S = 0, E = L;
  while((!(strData[S] > ' ') || !(strData[E] > ' ')) && (S >= 0) && (S <= L) && (E >= 0) && (E <= L))
  {
    if(strData[S] <= ' '){ S++; }
    if(strData[E] <= ' '){ E--; }
  }
  if(S == 0 && E == L){ return strData; } // Nothing to be done
  if((S >= 0) && (S <= L) && (E >= 0) && (E <= L)){
    L = E - S + 1;
    memmove(strData,&strData[S],L); strData[L] = '\0';
  }else{ strData[0] = '\0'; }
  return strData;
}

أنا أقوم فقط بتضمين الكود لأن الكود المنشور حتى الآن يبدو دون المستوى الأمثل (وليس لدي مندوب للتعليق عليه حتى الآن.)

void inplace_trim(char* s)
{
    int start, end = strlen(s);
    for (start = 0; isspace(s[start]); ++start) {}
    if (s[start]) {
        while (end > 0 && isspace(s[end-1]))
            --end;
        memmove(s, &s[start], end - start);
    }
    s[end - start] = '\0';
}

char* copy_trim(const char* s)
{
    int start, end;
    for (start = 0; isspace(s[start]); ++start) {}
    for (end = strlen(s); end > 0 && isspace(s[end-1]); --end) {}
    return strndup(s + start, end - start);
}

strndup() هو امتداد جنو.إذا لم يكن لديك ذلك أو ما يعادله، فقم بلفه بنفسك.على سبيل المثال:

r = strdup(s + start);
r[end-start] = '\0';

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

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

char *trimStr(char *str){
char *tmp = str;
printf("input string %s\n",str);
int nc = 0;

while(*tmp!='\0'){
  if (*tmp != ' '){
  nc++;
 }
 tmp++;
}
printf("total nonempty characters are %d\n",nc);
char *trim = NULL;

trim = malloc(sizeof(char)*(nc+1));
if (trim == NULL) return NULL;
tmp = str;
int ne = 0;

while(*tmp!='\0'){
  if (*tmp != ' '){
     trim[ne] = *tmp;
   ne++;
 }
 tmp++;
}
trim[nc] = '\0';

printf("trimmed string is %s\n",trim);

return trim; 
 }


int main(void){

char str[] = " s ta ck ove r fl o w  ";

char *trim = trimStr(str);

if (trim != NULL )free(trim);

return 0;
}

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

#include <ctype.h>
#include <string.h>
void trim_str(char *s)
{
    const size_t s_len = strlen(s);

    int i;
    for (i = 0; i < s_len; i++)
    {
        if (!isspace( (unsigned char) s[i] )) break;
    }

    if (i == s_len)
    {
        // s is an empty string or contains only space characters

        s[0] = '\0';
    }
    else
    {
        // s contains non-space characters

        const char *non_space_beginning = s + i;

        char *non_space_ending = s + s_len - 1;
        while ( isspace( (unsigned char) *non_space_ending ) ) non_space_ending--;

        size_t trimmed_s_len = non_space_ending - non_space_beginning + 1;

        if (s != non_space_beginning)
        {
            // Non-space characters exist in the beginning of s

            memmove(s, non_space_beginning, trimmed_s_len);
        }

        s[trimmed_s_len] = '\0';
    }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top