Ön / arka boşlukları standart bir şekilde nasıl düzeltirim?


178

C'deki bir ipin ön ve arka boşluklarını düzeltmek için temiz, tercihen standart bir yöntem var mı? Kendi başıma yuvarlanırdım, ama bunun eşit derecede yaygın bir çözümde ortak bir sorun olduğunu düşünürdüm.

Yanıtlar:


164

Dizeyi değiştirebiliyorsanız:

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

Dizeyi değiştiremiyorsanız, temel olarak aynı yöntemi kullanabilirsiniz:

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

6
Maalesef, bellek sızıntılarını önemsemediğiniz sürece ilk cevap hiç iyi değil. Artık üst üste binen iki dizeniz var (sondaki boşlukları kesilmiş olan orijinal ve yeni olan). Yalnızca orijinal dize serbest bırakılabilir, ancak bunu yaparsanız, ikincisi serbest hafızaya işaret eder.
David Nehme

7
@nvl: Ayrılan bellek yok, bu nedenle boşaltılacak bellek yok.
Adam Rosenfield

15
@nvl: No. stryerel bir değişkendir ve değiştirilmesi, iletilen orijinal işaretçiyi değiştirmez. C'deki işlev çağrıları her zaman değere göre değişir, hiçbir zaman referansa göre geçilmez.
Adam Rosenfield

11
@Raj: Aktarılmış olandan farklı bir adres döndürmenin doğasında yanlış bir şey yok. Burada, döndürülen değerin free()işlevin geçerli bir argümanı olması şartı yoktur . Tam tersi - verimlilik için bellek ayırma ihtiyacını önlemek için bunu tasarladım. Aktarılan adres dinamik olarak tahsis edilmişse, arayan kişi hala bu hafızayı boşaltmaktan sorumludur ve çağıranın bu değere burada döndürülen değerle yazmamaya dikkat etmesi gerekir.
Adam Rosenfield

3
İçin argümanını kullanmanız isspacegerekir unsigned char, aksi takdirde tanımlanmamış davranışları çağırırsınız.
Roland Illig

37

İşte dizeyi ara belleğinizin ilk konumuna kaydırır. Dizeyi dinamik olarak ayırdıysanız, yine de trim () öğesinin döndürdüğü işaretçide serbest bırakabilmeniz için bu davranışı isteyebilirsiniz:

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( frontp != str && endp == frontp )
            *str = '\0';
    else if( str + len - 1 != endp )
            *(endp + 1) = '\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;
}

Doğruluğunu test edin:

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

/* Paste function from above here. */

int main()
{
    /* The test prints the following:
    [nothing to trim] -> [nothing to trim]
    [    trim the front] -> [trim the front]
    [trim the back     ] -> [trim the back]
    [    trim front and back     ] -> [trim front and 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]
    [] -> []
    */

    char *sample_strings[] =
    {
            "nothing to trim",
            "    trim the front",
            "trim the back     ",
            "    trim front and back     ",
            " trim one char front and back ",
            " trim one char front",
            "trim one char back ",
            "                   ",
            " ",
            "a",
            "",
            NULL
    };
    char test_buffer[64];
    char comparison_buffer[64];
    size_t index, compare_pos;

    for( index = 0; sample_strings[index] != NULL; ++index )
    {
        // Fill buffer with known value to verify we do not write past the end of the string.
        memset( test_buffer, 0xCC, sizeof(test_buffer) );
        strcpy( test_buffer, sample_strings[index] );
        memcpy( comparison_buffer, test_buffer, sizeof(comparison_buffer));

        printf("[%s] -> [%s]\n", sample_strings[index],
                                 trim(test_buffer));

        for( compare_pos = strlen(comparison_buffer);
             compare_pos < sizeof(comparison_buffer);
             ++compare_pos )
        {
            if( test_buffer[compare_pos] != comparison_buffer[compare_pos] )
            {
                printf("Unexpected change to buffer @ index %u: %02x (expected %02x)\n",
                    compare_pos, (unsigned char) test_buffer[compare_pos], (unsigned char) comparison_buffer[compare_pos]);
            }
        }
    }

    return 0;
}

Kaynak dosya trim.c idi. 'Cc -Wall trim.c -o trim' ile derlenmiştir.


2
İçin argümanını kullanmanız isspacegerekir unsigned char, aksi takdirde tanımlanmamış davranışları çağırırsınız.
Roland Illig

@RolandIllig: Teşekkürler, bunun gerekli olduğunu hiç anlamadım. Onu düzeltti.
indiv

@Simas: Bunu neden söylüyorsun? Fonksiyon çağırıyor ve isspace()neden " "ve arasında bir fark olsun ki "\n"? Yeni satırlar için birim testleri ekledim ve bana iyi görünüyor ... ideone.com/bbVmqo
indiv

1
@indiv, manuel olarak tahsis edildiğinde geçersiz bellek bloğuna erişecektir. Yani bu satırı: *(endp + 1) = '\0';. Yanıttaki örnek testte, bu sorunu önleyen 64'lük bir tampon kullanılır.
Simas

1
@nolandda: Detay için teşekkürler. Düzelttim ve şu anda valgrind'e erişimim olmadığından arabellek taşmasını algılamak için testi güncelledim.
indiv

23

Çözümüm. Dize değiştirilebilir olmalıdır. Daha sonra serbest bırakmanız () durumunda eski işaretçiyi kullanmaya devam edebilmeniz için boşluk olmayan kısmı başa doğru hareket ettiren diğer bazı çözümlerin üzerindeki avantaj.

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

Bu sürüm, dizeyi yerinde düzenlemek yerine strndup () ile bir kopyasını oluşturur. strndup () _GNU_SOURCE gerektirir, bu nedenle malloc () ve strncpy () ile kendi strndup () 'nızı yapmanız gerekebilir.

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

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

    return strndup(s, l);
}

4
trim()çağıran UB eğer solduğunu ""ilk olarak isspace()çağrı olacağını isspace(p[-1])ve p[-1]mutlaka yasal bir konuma başvuruda bulunmuyor.
chux - Monica

1
İçin argümanını kullanmanız isspacegerekir unsigned char, aksi takdirde tanımlanmamış davranışları çağırırsınız.
Roland Illig

1
if(l==0)return;uzunlukta str önlemek için eklemeniz gerekir
ch271828n

11

Burada, sol, sağ, her ikisi, hepsi, yerinde ve ayrı kırpmak ve bir dizi karakter (veya varsayılan olarak beyaz boşluk) kırpmak için C mini kütüphanem.

strlib.h içeriği:

#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 içeriği:

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

Bir ana rutin her şeyi yapar. Teller varsa yerine oturur.Src == dst oturur , aksi takdirde strcpyrutinler gibi çalışır . Dize sınırlamasında belirtilen bir dizi karakteri düzeltir boşsa veya boşsa beyaz boşluk . Sola, sağa, her ikisine ve tümünü keser (tr gibi). Çok fazla bir şey yok ve dize üzerinde sadece bir kez yineleniyor. Bazı kişiler, sağdan trim ayarının soldan başladığından şikayet edebilirler, ancak yine de soldan başlayan stile gerek yoktur. (Doğru düzeltmeler için ipin sonuna gelmek için bir şekilde veya böyle yapmanız gerekir, böylece işi gittiğiniz gibi de yapabilirsiniz.) Boru hattı ve önbellek boyutları ve bu tür - kim bilir . Çözüm soldan sağa doğru çalıştığı ve yalnızca bir kez yinelendiği için akışlar üzerinde de çalışacak şekilde genişletilebilir. Sınırlamalar: Bu mu değil üzerinde çalışmak unicode


2
Bunu iptal ettim ve eski olduğunu biliyorum ama sanırım bir hata var. dtab[*d]döküm gelmez *diçin unsigned intbir dizi endeks olarak kullanmadan önce. İmzalı karakter bulunan bir sistemde bu, dtab[-127]hatalara ve muhtemelen çökmelere neden olacak şekilde okunacaktır .
Zan Lynx

2
Potansiyel tanımsız davranış dtab[*delim++]nedeniyle charendeks değerleri dökülmesi gerekir unsigned char. Kod 8 bit olduğunu varsayar char. delimolarak bildirilmelidir const char *. dtab[0xFF & (unsigned int)*d]olarak daha net olurdu dtab[(unsigned char)*d]. Kod UTF-8 kodlu dizelerde çalışır, ancak ASCII olmayan aralık dizilerini çıkarmaz.
chqrlie

@ michael-plainer, bu ilginç görünüyor. Neden test edip GitHub'a koymuyorsunuz?
Daisuke Aramaki

9

İşte basit ama doğru bir yerinde düzeltme işlevini denemem.

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

2
"" Str [-1] ` while ((end >= begin) && isspace(str[end]))olduğunda UB'yi önlemek için olarak değiştirmenizi öneririz . str is . Prevents
chux - Monica

Btw, çalışmak için bunu str [i - begin + 1] olarak değiştirmek zorundayım
truongnm

1
İçin argümanını kullanmanız isspacegerekir unsigned char, aksi takdirde tanımlanmamış davranışları çağırırsınız.
Roland Illig

@RolandIllig, neden tanımsız davranışlar olsun ki? Bu fonksiyon karakterlerle çalışmak üzere tasarlanmıştır.
wovano

@wovano Hayır, değil. İşlevlerinin , özel değeri veya değerini <ctype.h>temsil eden ints ile çalışması amaçlanmıştır . Bkz. Stackoverflow.com/q/7131026/225757 . unsigned charEOF
Roland Illig

8

Trim partisine geç

Özellikler:
1. Diğer cevapların birçoğu gibi hızlı bir şekilde başlangıcı kesin.
2. Sona gittikten sonra, döngü başına sadece 1 test ile sağa doğru düzeltme yapın. @ Jfm3 gibi, ama hepsi beyaz boşluk dize için işleri)
3. zaman tanımsız davranışı engellemek için charbir imzalanır char, döküm *siçin unsigned char.

Karakter işleme "Her durumda argüman bir intdeğeridir unsigned char, değeri makrosu ile temsil edilebilir veya makronun değerine eşit olacaktır EOF. Bağımsız değişken başka bir değere sahipse, davranış tanımsızdır." 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 desired, shift the trimmed string

  return s;
}

@chqrlie yukarıda kesilen dize kaydırmaz yorumladı. Böyle yaparak....

// 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);   // older errant code
    len = (size_t) (p - s + 1);  // Thanks to @theriver
  }

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

3
Yay, nihayet ctype tanımsız davranışı bilen biri.
Roland Illig

2
@chux Bence len = (size_t) (ps) +1 olmalı; aksi takdirde son harf çakışır.
theriver

4

İşte @ adam-rosenfields yerinde değişiklik rutinine benzer, ancak gereksiz yere strlen () 'e başvurmadan bir çözüm. @Jkramer gibi, dize arabellek içinde sola ayarlanır, böylece aynı işaretçiyi serbest bırakabilirsiniz. Memmove kullanmadığı için büyük dizeler için uygun değildir. @ Jfm3'ün bahsettiği ++ / - operatörlerini içerir. FCTX tabanlı birim testleri dahildir.

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

Bu çözüm tamamen tehlikelidir! Orijinal dize boşluk olmayan karakterler içermiyorsa, bu baytlar 'boşluk' baytları içeriyorsa, son kırpma satırı a'nın önünde olan her şeyin üzerine yazar. Bunu optimizasyon olmadan derleyin ve y'ye ne olduğunu görün: unsigned x = 0x20202020; char s [4] = ""; imzasız y = 0x20202020; printf ("& x, & s, & y =% p,% p,% p \ n", & x, & s, & y); printf ("x, [s], y =% 08x, [% s],% 08x \ n", x, s, y); trim_whitespace (s); printf ("x, [s], y =% 08x, [% s],% 08x \ n", x, s, y);
Villemoes

@Villemoes, hata raporu için teşekkür ederim. Dize sadece boşluk içerdiğinde arabellek sol tarafında yürüyüş önlemek için mantığı güncelledim. Bu yeni sürüm endişelerinizi karşılıyor mu?
Rhys Ulerich

Dil avukatları muhtemelen 'a' işaretinden önceki karaktere bir işaretçi oluşturma hakkında spekülasyon düşünmek için sana bağırırdı ('--p' nin yapacağı şey budur). Gerçek dünyada, muhtemelen iyisin. Ancak '> =' öğesini '>' olarak değiştirebilir ve p'nin düşüşünü 'isspace (* - p)' değerine taşıyabilirsiniz.
Villemoes

Bence avukatlar bir adresi dokunmadan karşılaştırıyorlar, çünkü bu ipucundaki önerilerinizi de beğeniyorum. Buna göre güncelledim. Teşekkürler.
Rhys Ulerich

1
doukremt, foobardan sonraki tüm tamponun sıfırlarla doldurulmaması endişeniz mi? Eğer öyleyse, belirsiz kayalar atmak yerine bu kadar açık bir şekilde söylediyseniz biraz daha yararlı olurdu.
Rhys Ulerich

3

Bir diğeri, bir satır gerçek işi yapıyor:

#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
Scanf kullanmak için iyi bir fikir; ancak yalnızca OP'nin istediği gibi olmayan tek bir sözcükle çalışacaktır (yani "abc" yi düzeltmek "ab c" ile sonuçlanırken, tek scanf'iniz sadece "a" ile sonuçlanır). Bu yüzden, bir döngüye ihtiyacımız var ve %ndönüşüm belirteciyle atlanan grafikler için bir sayaca ihtiyacımız var ve sonunda bunu elle yapmak daha basit, korkarım.
Peter - Monica'yı

İlk boşlukları göz ardı ederek dizenin ilk kelimesini istediğinizde çok kullanışlıdır.
J ... S

3

Bu cevapların çoğunu beğenmedim çünkü aşağıdakilerden bir veya daha fazlasını yaptılar ...

  1. Orijinal işaretçinin dizesinin içinde farklı bir işaretçi döndürdü (iki farklı işaretçiyi aynı şeye dönüştürmek için bir tür acı).
  2. Tüm dizeyi önceden yineleyen strlen () gibi şeyleri ücretsiz olarak kullandı .
  3. Taşınabilir olmayan işletim sistemine özgü lib işlevleri kullanıldı.
  4. Backscanned.
  5. TAB / CR / LF korunacak şekilde isspace () yerine '' ile karşılaştırma kullanılır .
  6. Büyük statik tamponlarla boşa giden bellek.
  7. Sscanf / sprintf gibi yüksek maliyetli işlevlere sahip atık döngüleri .

İşte benim sürüm:

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

2
İçin argümanını kullanmanız isspacegerekir unsigned char, aksi takdirde tanımlanmamış davranışları çağırırsınız.
Roland Illig

Bu cevap "Boşa giden döngüler" ile ilgili olduğundan, boş alan olmadığında kodun tüm sokmayı gereksiz yere kopyaladığını unutmayın. Bir lider while (isspace((unsigned char) *szWrite)) szWrite++;bunu engelleyecektir. Kod ayrıca tüm boşlukları kopyalar.
chux - Monica

@chux bu uygulama, ayrı okuma ve yazma işaretçileriyle (farklı bir konumda yeni bir işaretçi döndürmenin aksine) yerinde mutasyona uğrar, bu nedenle szWrite'ı birinci satırdaki ilk boşluğa atlatma önerisi, orijinal dize.
Jason Stewart

@chux, sondaki boşluk (son boşluk olmayan karakterden sonra bir boş değer eklemeden önce) kopyaladığını doğruyorsunuz, ancak dizeyi önceden taramaktan kaçınmak için ödemeyi seçtiğim fiyat bu. Mütevazı miktarda WS için, son WS olmayan karakter için tüm dizeyi önceden taramak yerine sadece baytları kopyalamak daha ucuzdur. Büyük miktarda takip eden WS için, ön tarama muhtemelen yazma işlemlerinde azalmaya değer olacaktır.
Jason Stewart

@chux, "boşluk olmadığında kopyalar" durumu için, yalnızca *szWrite = *szReadişaretçiler eşit olmadığında performans göstermek bu durumda yazmaları atlar, ancak daha sonra başka bir karşılaştırma / dal ekledik. Modern CPU / MMU / BP ile, bu kontrolün bir kayıp veya kazanç olup olmadığı hakkında hiçbir fikrim yok. Daha basit işlemciler ve bellek mimarileri ile, sadece kopyayı yapmak ve karşılaştırmayı atlamak daha ucuzdur.
Jason Stewart

2

Partiye çok geç ...

Geri izlemesiz tek geçişli ileri tarama çözümü. Kaynak dizgideki her karakter tam olarak iki kez test edilir . (Bu nedenle, özellikle kaynak dizede çok fazla boşluk varsa, buradaki diğer çözümlerin çoğundan daha hızlı olmalıdır.)

Bu, biri kaynak dizgiyi başka bir hedef dizeye kopyalamak ve kırpmak için diğeri ise kaynak dizeyi yerinde kırpmak için iki çözüm içerir. Her iki işlev de aynı kodu kullanır.

(Değiştirilebilir) dize yerinde taşınır, böylece orijinal işaretçi değişmeden kalır.

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

1
Kaynak dizgideki her karakter tam olarak bir kez test edilir : gerçekten değil, kaynak dizgideki çoğu karakter iki kez test edilir: ile karşılaştırıldığında '\0've sonra test edilir isspace(). Tüm karakterleri test etmek israf gibi görünüyor isspace(). İpin sonundan geri takip, patolojik olmayan vakalar için daha etkili olmalıdır.
chqrlie

@chqrlie - Evet, her karakter iki kez test ediliyor. Bu kodun gerçekten test edildiğini görmek istiyorum, özellikle burada diğer algoritmalara kıyasla çok fazla boşluk içeren dizeler verildi.
David R Tribble

trim()TAMAM. Köşe vaka: trim2(char *d, const char *s)sorun vardır d,sörtüşme ve s < d.
chux - Monica

@chux - Bu köşe durumunda nasıl trim()davranmalı? Bir dizeyi, dizenin kendisinin kapladığı belleğe kırpıp kopyalamak istiyorsunuz. Bunun aksine memmove(), düzeltme işlemini yapmadan önce kaynak dizginin uzunluğunun belirlenmesini gerektirir; bu, tüm dizgiyi ek bir kez taramayı gerektirir. rtrim2()Kaynağı hedefe geriye doğru kopyalamayı bilen ve muhtemelen ek bir kaynak dize uzunluğu argümanı alan farklı bir işlev yazmak daha iyidir.
David R Tribble

1

Ne "ağrısız" diye düşündüğünüzden emin değilim.

C telleri oldukça acı vericidir. İlk boşluk olmayan karakter konumunu önemsiz bulabiliriz:

(isspace (* p)) p ++;

Son boşluk olmayan karakter konumunu iki benzer önemsiz hareketle bulabiliriz:

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

(Sana *ve++ Aynı anda operatörleri operatörleri .)

Şimdi soru şu: bununla ne yaparsın? Eldeki veri türü gerçekten Stringdüşünülmesi kolay büyük bir sağlam soyut değil, bunun yerine bir dizi depolama baytından çok az. Sağlam bir veri türü olmadığından, PHperytonby'nin chompişleviyle aynı işlevi yapacak bir işlev yazmak imkansızdır . C'de böyle bir işlev ne döndürür?


Dize tüm beyaz boşluklardan oluşmadıkça bu işe yarar. Bilmeniz için bir kez kontrol do { q--; } ...etmeniz gerekiyor *q != 0.
chux - Monica

1

Bir dize kütüphanesi kullanın , örneğin:

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

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

... bunun "yaygın" bir sorun olduğunu söylediğiniz gibi, evet #include ya da benzeri eklemeniz gerekir ve libc'ye dahil edilmez, ancak rastgele işaretçiler ve boyutlar saklayan kendi kesmek işinizi icat etmeyin. arabellek taşmaları.



1

Sadece bu büyümeyi sürdürmek için, değiştirilebilir bir dize ile bir seçenek daha:

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

1
strlen()size_taralığını aşabilecek bir a döndürür int. boşluk, boşluk karakteriyle sınırlı değildir. Son olarak ama en önemlisi: strcpy(string, string + i * sizeof(char));Kaynak ve hedef diziler çakıştığı için tanımlanmamış davranış . Yerine memmove()kullanın strcpy().
chqrlie

@chqrlie haklısınız, sadece önerilerinizi dahil ettiniz. Kaynak ve hedef çakışması olduğunda kopyalamanın tanımsız davranışa neden olabileceğini anlıyorum, ancak bu özel durumda bunun her zaman bir sonraki konumdan başlangıca kopyalayacağımız için herhangi bir soruna neden olmaması gerektiğini belirtmek istiyorum, Geri dönüşünüz için teşekkür ederiz.
wallek876

1
kaynak ve hedef dizilerin nasıl çakıştığı önemli değil, tanımsız bir davranış. Kopyalamanın artan adresler boyunca her seferinde bir bayt olabileceği varsayımına güvenmeyin. Ayrıca while (isspace((int)string[i])) string[i--] = '\0';dize başının ötesinde döngü olabilir bahsetmeyi unuttum . Bu döngüyü önceki ve sonraki satırlarla birleştirip yazmalısınızwhile (i > 0 && isspace((unsigned char)string[--i])) { string[i] = '\0'; } size_t end = i;
chqrlie

@chqrlie iyi bir nokta, tüm beyaz boşlukları olan bir dize başlangıçtan dönmeye neden olurdu, bunu düşünmemiştim.
wallek876

Aslında, endönermem boş byte'ı işaret etmediği için önerim yanlıştı ve end = ++i;hala tüm boşluk karakterlerini içeren dizeler için bir sorun vardı. Kodu düzelttim.
chqrlie

1

Birçok cevabım olduğunu biliyorum, ancak çözümümün yeterince iyi olup olmadığını görmek için cevabımı buraya gönderiyorum.

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

2
Not: isspace(*str)Ne zaman *str < 0.
chux - Monica

1
Kullanımı size_t niyidir, ancak arabirim, nkesilmiş bir dize için çok küçük olduğunda arayanı hiçbir şekilde bilgilendirmez . Düşününtrim(out, 12, "delete data not")
chux - Monica

1

Bir dizede önde gelen boşlukları atlamanın en kolay yolu imho,

#include <stdio.h>

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

1
Bu, ortasında boşlukları olan dizeler için çalışmaz " foo bar ".
David R Tribble

1

Tamam bu soruyu benim üstlenmem. Ben yerinde dize değiştirir ( freeişe yarayacak) ve herhangi bir UB önler en özlü çözüm olduğuna inanıyorum . Küçük dizeler için, büyük olasılıkla memmove içeren bir çözümden daha hızlıdır.

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

b > strTest sadece bir kez gereklidir. *b = 0;sadece bir kez gerekli.
chux - Monica

1
#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 tüm beyaz alanların kırpılmasına yardımcı olur.

  • Son bayttan boşluk karakteri olup olmadığını kontrol etmek ve uzunluk değişkenini azaltmak için bir ilk döngü çalıştırın
  • İlk bayttan boşluk karakteri olup olmadığını kontrol etmek ve uzunluk değişkenini ve karakter işaretçisini artırmak için ikinci bir döngü çalıştırın.
  • Son olarak length değişkeni 0'dan büyükse strndupboşlukları hariç tutarak yeni dize arabelleği oluşturmak için kullanın .

Sadece küçük bir nitpick, strndup()C standardının bir parçası değil, sadece Posix. Ancak uygulaması oldukça kolay olduğundan büyük bir sorun değildir.
Patrick Schlüter

trim_space("")döner NULL. Bir işaretçi beklerdim "". int len;olmalı size_t len;. isspace(in[len - 1])UB ne zaman in[len - 1] < 0.
chux - Monica

Daha while (isspace((unsigned char) *in) in++;önce bir başlangıç len = strlen(in);daha sonradan daha verimli olurduwhile(len && *in && isspace(*in)) ++in, --len;
chux - Monica'yı yeniden eski haline getir

0

Şahsen ben kendim dönerdim. Strtok'u kullanabilirsiniz, ancak hangi belleğin ne olduğunu bildiğinizden (özellikle önde gelen karakterleri kaldırıyorsanız) dikkatli olmanız gerekir.

Son boşluklardan kurtulmak için son alanın üst kısmına 0 koyabileceğinizden, sondaki boşluklardan kurtulmak kolay ve oldukça güvenlidir. Önde gelen alanlardan kurtulmak, işleri hareket ettirmek anlamına gelir. Bunu yerinde yapmak istiyorsanız (muhtemelen mantıklı), öncü alan kalmayıncaya kadar her şeyi bir karakter geri kaydırmaya devam edebilirsiniz. Daha verimli olmak için, ilk boşluk olmayan karakterin dizinini bulabilir ve her şeyi bu sayıya kaydırabilirsiniz. Veya, ilk boşluk olmayan karakter için bir işaretçi kullanabilirsiniz (ancak sonra strtok ile aynı şekilde dikkatli olmanız gerekir).


4
strtok genellikle kullanmak için çok iyi bir araç değildir - en azından yeniden giriş yapmadığı için. Tek bir fonksiyonun içinde kalırsanız, güvenle kullanılabilir, ancak iplik veya kendiliğinden strtok kullanabilecek başka fonksiyonlar çağırma olasılığı varsa, sorun yaşarsınız.
Jonathan Leffler

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

3
Bu beni güldürdü çünkü dreamlax'ın test dizesini "büyük zaman berbat" ı içerecek şekilde düzenlediğini sanıyordum. Hayır! Orijinal yazar sadece dürüst.
James Morris

1
Bu kodu kullanmayın. Bir arabellek taşması üretir.
Roland Illig

0

Oyuna biraz geç kaldım, ama rutinlerimi savaşa atacağım. Muhtemelen en mutlak verimli değiller, ama doğru olduklarına ve basit olduklarına inanıyorum ( rtrim()karmaşıklık zarfını iterek):

#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
Eğer kullanması gereklidir charetmek argüman isspace()için (unsigned char)olumsuz değerler üzerinde tanımsız davranışı önlemek için. Ayrıca ltrim()gerekli değilse dizeyi hareket ettirmekten kaçının .
chqrlie

0

Şimdiye kadar cevapların çoğu aşağıdakilerden birini yapıyor:

  1. Dizenin sonundaki geriye doğru izleme (yani dizenin sonunu bulun ve boşluk olmayan bir karakter bulunana kadar geriye doğru arama yapın) veya
  2. strlen()İlk olarak tüm dizeden ikinci bir geçiş yaparak arayın .

Bu sürüm yalnızca bir geçiş yapar ve geri izlemez. Bu yüzden diğerlerinden daha iyi performans gösterebilir, ancak yalnızca yüzlerce sondaki boşluğa sahip olmak yaygınsa (SQL sorgusunun çıktısıyla uğraşırken olağandışı değildir).

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

1
Performans kuşkularınız varsa, kullanmayın strspn()ve strcspn()sıkı bir döngü içinde. Bu çok verimsizdir ve genel gider, tek ileri geçişin kanıtlanmamış avantajını gölgede bırakacaktır. strlen()genellikle gerçek bir endişe değil, çok verimli kod ile satır içi genişletilir. Dizenin başlangıcını ve sonunu kırpmak, çok az beyaz karaktere sahip olan veya hiç olmayan özel dizelerde bile dizedeki her karakteri beyazlık açısından test etmekten çok daha hızlı olacaktır.
chqrlie

0

Bu düşünebildiğim en kısa uygulama:

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
}

1
Buna ne dersiniz:char *trim(char *s) { char *p = s, *e = s + strlen(s); while (e > s && isspace((unsigned char)e[-1])) { *--e = '\0'; } while (isspace((unsigned char)*p)) { p++; } if (p > s) { memmove(s, p, e + 1 - p); } return s; }
chqrlie

0

Bu işlevler orijinal arabelleği değiştirir, bu nedenle dinamik olarak tahsis edilirse orijinal işaretçi serbest bırakılabilir.

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

rstrip()boş dizede tanımsız davranış çağırır. lstrip()boşluk karakterlerinin uzun bir başlangıç ​​kısmı ile dizede gereksiz yere yavaştır. isspace()bir charargüman geçirilmemelidir, çünkü negatif değerlerde farklı tanımlanmamış davranışlar başlatır EOF.
chqrlie


0

Her iki taraftan tellerimi kesmek için yaşlıyı ama gooody'yi kullanıyorum;) Ascii ile her şeyi bir boşluktan daha az kesebilir, yani kontrol karakterleri de kesilir!

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

Bunun size_tyerine kullanmalısınız unsigned int. Kodun fazladan fazla testi vardır ve strncpy(strData,&strData[S],L)kaynak ve hedef diziler çakıştığından tanımsız davranışlar başlatır . Yerine memmove()kullanın strncpy().
chqrlie

Bu durumda hedef adres her zaman kaynaktan daha küçük bir dizine sahip olduğu için sorun olmaz, ancak evet memmove gerçekten daha iyi olacaktır.
Деян Добромиров

hayır sorun değil. kaynak ve hedef dizilerin nasıl üst üste geldiği önemli değildir, tanımlanmamış davranışı başlatır, çünkü kütüphane işlevlerinin standart özelliklerinin ötesinde uygulanması konusunda güvenli bir şekilde varsayımlar yapamazsınız. Modern derleyiciler, potansiyel tanımlanmamış davranışları olan durumlardan haksız yararlanma, güvenli oynama ve UB'den uzak durma ve yeni başlayanların güvenli olmayan varsayımlar yapmasına izin verme eğilimindedir.
chqrlie

0

Şimdiye kadar yayınlanan kod yetersiz görünüyor (ve henüz yorum için temsilcisi yok çünkü sadece kod dahil ediyorum.)

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()bir GNU oluşumudur. Elinizde veya eşdeğeri bir şey yoksa, kendinizinkini yuvarlayın. Örneğin:

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

1
isspace(0)yanlış olarak tanımlanır, her iki işlevi de basitleştirebilirsiniz. Ayrıca bloğun memmove()içini hareket ettirin if.
chqrlie

0

Burada trimStr işlevine giriş dizesini kırpmak için dinamik bellek ayırma kullanın. İlk olarak, giriş dizesinde kaç tane boş olmayan karakter bulunduğunu buluruz. Ardından, bu boyutta bir karakter dizisi tahsis eder ve boş sonlandırılmış karaktere dikkat ederiz. Bu işlevi kullandığımızda, ana işlevin içindeki belleği boşaltmamız gerekir.

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

0

İşte böyle yapıyorum. Dizeyi düzeltir, böylece döndürülen bir dizeyi yeniden konumlandırma veya işaretçiyi ayrılmış bir dizeye kaybetme konusunda endişelenmeyin. Mümkün olan en kısa cevap olmayabilir, ancak çoğu okuyucu için açık olmalıdır.

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

okuyucular için kesinlikle net, ancak strlen başka bir döngü gerçekleştirir .. :)
ingconti

0
char* strtrim(char* const str)
{
    if (str != nullptr)
    {
        char const* begin{ str };
        while (std::isspace(*begin))
        {
            ++begin;
        }

        auto end{ begin };
        auto scout{ begin };
        while (*scout != '\0')
        {
            if (!std::isspace(*scout++))
            {
                end = scout;
            }
        }

        auto /* std::ptrdiff_t */ const length{ end - begin };
        if (begin != str)
        {
            std::memmove(str, begin, length);
        }

        str[length] = '\0';
    }

    return str;
}

2
Bu kod soruyu cevaplayabilirken, sorunun nasıl ve / veya neden çözüldüğüne dair ek bağlam sağlamak yanıtlayıcının uzun vadeli değerini artıracaktır.
Nic3500
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.