it-swarm-eu.dev

Jak lze standardním způsobem upravit začátek/koncové mezery?

Existuje čistá, nejlépe standardní metoda ořezávání předních a koncových mezer z řetězce v C? Řekl bych si vlastní, ale myslím si, že je to společný problém s stejně běžným řešením.

156
coledot

Pokud můžete upravit řetězec:

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

Pokud řetězec nemůžete změnit, můžete použít v podstatě stejnou metodu:

// 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;
}
148
Adam Rosenfield

Zde je ten, který přesune řetězec do první pozice bufferu. Možná budete chtít toto chování, takže pokud dynamicky přidělíte řetězec, můžete jej stále uvolnit ve stejném ukazateli, který trim () vrátí:

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

Test správnosti:

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

Zdrojový soubor byl trim.c. Zkompilovaný s 'cc trim.c -o trim'.

31
indiv

Moje řešení. Řetězec musí být vyměnitelný. Výhoda nad některými jinými řešeními, že přesune ne-prostorovou část na začátek, takže můžete stále používat starý ukazatel, v případě, že je budete muset později uvolnit ().

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

Tato verze vytvoří kopii řetězce s strndup () namísto úpravy na místě. strndup () vyžaduje _GNU_SOURCE, takže možná budete muset vytvořit vlastní strndup () s malloc () a 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);
}
20
jkramer

Zde je moje C mini knihovna pro oříznutí vlevo, vpravo, obojí, vše, na místě a oddělené, a oříznutí sady zadaných znaků (nebo mezer ve výchozím nastavení).

obsah 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

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

Jediná hlavní rutina to všechno dělá. Je-li src == dst, jinak, Funguje tak, že funguje jako rutiny strcpy. Prochází množinu znaků zadaných v řetězec delim, nebo bílé místo, pokud je null. Zkrácení vlevo, vpravo, obojího a všeho (jako tr). Není toho mnoho, a to opakuje řetězec pouze jednou. Někteří lidé si mohou stěžovat, že trimování začíná na levé straně, nicméně není zapotřebí žádný strlen, který začíná na levé straně. (Jedním nebo druhým způsobem se musíte dostat na konec řetězce pro správné okraje, takže můžete dělat i práci tak, jak jdete.) Mohou existovat argumenty týkající se pipeliningu a velikosti mezipaměti a podobně - kdo ví . Vzhledem k tomu, že řešení funguje zleva doprava a iteruje pouze jednou, lze jej rozšířit i na streamování. Omezení: dělá ne práce na unicode strunách.

9
Shoots the Moon

Zde je můj pokus o jednoduchou, ale správnou funkci trimování na místě.

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

Pozdívka ke střihu

Funkce:
1. Začněte rychle, jako v řadě dalších odpovědí.
2. Poté, co přejdete na konec, ořízněte právo pouze jedním testem na smyčku. Jako @ jfm3, ale funguje pro všechny řetězce bílého prostoru)
3. Chcete-li se vyhnout nedefinovanému chování, když char je podepsaný char, cast *s na unsigned char

Zpracování znaků "Ve všech případech je argument int, jehož hodnota musí být reprezentovatelná jako unsigned char nebo se musí rovnat hodnotě makra EOF. Pokud má argument jinou hodnotu, chování je nedefinováno." 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 komentované výše neposouvá oříznutý řetězec. Udělat to tak....

// 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);
}
5
chux

Další, jedna linka dělá skutečnou práci:

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

Většina z těchto odpovědí se mi nelíbila, protože jedna nebo více z následujících ...

  1. Vrátil jiný ukazatel uvnitř řetězce původního ukazatele (druh bolesti k žonglování dvou různých ukazatelů na stejnou věc).
  2. Bezdůvodně se používají věci jako strlen () , které pre-iterují celý řetězec.
  3. Používá nepřenosné funkce lib specifické pro OS.
  4. Backscanned.
  5. Používá se srovnání s '' namísto isspace () , takže TAB/CR/LF jsou zachovány.
  6. Promrhaná paměť s velkými statickými buffery.
  7. Promrhané cykly s vysoce nákladnými funkcemi jako sscanf/sprintf .

Zde je moje verze:

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';
}
3
Jason Stewart

Zde je řešení podobné rutinní modifikaci @ adam-rosenfields, ale bez zbytečného uchýlení se ke strlen (). Stejně jako @jkramer je řetězec upraven v rámci vyrovnávací paměti, takže můžete uvolnit stejný ukazatel. Není optimální pro velké řetězce, protože nepoužívá memmove. Zahrnuje ++/- operátory, které @ jfm3 uvádí. FCTX -zahrnuté testy jednotek.

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

Velmi pozdě na párty ...

Jednosměrné dopředné skenování bez zpětného dozadu. Každý znak ve zdrojovém řetězci je testován přesně jednou dvakrát. (Tak by to mělo být rychlejší než většina jiných řešení zde, zejména pokud zdrojový řetězec má spoustu koncových mezer.)

To zahrnuje dvě řešení, jedno kopírování a oříznutí zdrojového řetězce do jiného cílového řetězce a druhé oříznutí zdrojového řetězce na místě. Obě funkce používají stejný kód.

(Modifiable) řetězec je přesunut na místě, takže původní ukazatel na něj zůstává nezměněn.

#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);
}
2
David R Tribble

Nejjednodušší způsob, jak přeskočit mezery v řetězci, je imho,

#include <stdio.h>

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

Ok, tohle je moje otázka. Věřím, že je to nejpřesnější řešení, které modifikuje řetězec na místě (free bude fungovat) a vyhne se jakémukoli UB. Pro malé struny je to pravděpodobně rychlejší než řešení obsahující 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;
}
1
poby

Nejsem si jistý, co považujete za "bezbolestné".

C struny jsou docela bolestivé. První pozici znaku bez mezer nalezneme triviálně:

 zatímco (isspace (* p)) p ++; 

Nalezneme poslední pozici znaku bez mezer s dvěma podobnými triviálními pohyby:

[.], zatímco (* q) q ++; 
 do {q--; } zatímco (isspace (* q)); 

(Ušetřil jsem vám bolest při používání operátorů * a ++ současně.)

Otázkou nyní je, co s tím děláte? Datový typ po ruce není ve skutečnosti velký robustní abstraktní String, který lze snadno přemýšlet, ale místo toho je opravdu sotva více než pole úložných bytů. Pokud není k dispozici robustní datový typ, není možné zapsat funkci, která bude fungovat stejně jako funkce chomp funkce PHperytonby. Co by taková funkce v C vrátila?

1
jfm3

Jen aby tento růst rostl, ještě jedna možnost s modifikovatelným řetězcem:

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
wallek876
#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;
}
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 pomáhá zkrátit všechna bílá místa.

  • Spusťte první smyčku pro kontrolu z posledního bajtu pro znak mezery a snižte proměnnou délky
  • Spustit druhou smyčku zkontrolovat z první bajt znak mezery a snížit ukazatel délky proměnné a přírůstek znaku char.
  • Nakonec, pokud je proměnná length větší než 0, pak pomocí strndup vytvořte nový řetězec bufferu bez mezer.
1
rashok

Vím, že existuje mnoho odpovědí, ale svou odpověď zde uvádím, abych zjistil, zda je moje řešení dost dobré. 

// 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;
}
1
Ekeyme Mo

s byl tak nesmírně užitečný, chtěl jsem říct, že jsem rád, že tento post byl k dispozici a ukázat, co jsem byl schopen dělat s příklady. Potřeboval jsem tokenizovat větší řetězec a pak vzít dílčí řetězec (y) a najít ten poslední - tak jsem mohl odstranit nový řádek z fgets () volání a také odstranit mezery z přední strany token - tak jsem mohl snadno jej porovnat se statickým řetězcem. První příklad ve výše uvedeném příspěvku mě tam dostal, takže děkuji. Zde je návod, jak jsem použil vzorky kódu a výstup, který jsem dostal.

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;

}

Výstup

Soubor existuje.

Řetězec: Stav DBS: Spuštění DBS - inicializace konfigurace DBS

Stát DBS

Spuštění systému DBS

Spuštění systému DBS

Inicializace konfigurace DBS

Inicializace konfigurace DBS

Řetězce jsou stejné.

Tokenizing Hotovo.

1
Diana

Pokud používáte glib, můžete použít g_strstrip

1
sleepycal

Použít řetězec knihovny , například:

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

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

... jak říkáte, že se jedná o "obyčejný" problém, ano, musíte zahrnout #include nebo tak a není součástí libc, ale nechoďte si vymýšlet vlastní práci s hackem, která ukládá náhodné ukazatele a velikost_tto způsobem vede pouze k přetečení vyrovnávací paměti.

1
James Antill

Většina odpovědí má zatím jednu z následujících možností:

  1. Backtrack na konci řetězce (tj. Najděte konec řetězce a pak hledejte dozadu, dokud nenajdete znak bez mezery) nebo
  2. Nejdříve zavolejte strlen() a proveďte druhý průchod celým řetězcem.

Tato verze je pouze jeden průchod a není backtrack. Může tedy fungovat lépe než ostatní, i když je běžné, že mají stovky koncových mezer (což není neobvyklé, když se jedná o výstup dotazu 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';
}
0
finnw

Tyto funkce upraví původní vyrovnávací paměť, takže pokud je dynamicky přiděleno, může být uvolněn původní ukazatel .

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

Zde je, jak to dělám. Ořezává řetězec na místě, takže se nemusíte obávat, že se vrátí řetězec nebo ztrácí ukazatel na přidělený řetězec. Možná to není nejkratší možná odpověď, ale většina čtenářů by měla mít jasno.

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

Chcete-li zastřihnout struny z obou stran, používám oldie, ale gooody;) Může zkrátit cokoliv ascii méně než mezerou, což znamená, že i kontrolní znaky budou oříznuty!

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;
}
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;
}
0
Mitch Laber

Zde používám dynamické přidělení paměti pro oříznutí vstupního řetězce funkce trimStr. Nejprve zjistíme, kolik neprázdných znaků existuje ve vstupním řetězci. Pak přidělíme znakové pole této velikosti a postaráme se o nulově ukončený znak. Když tuto funkci použijeme, musíme uvolnit paměť uvnitř hlavní funkce. 

#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
saeed_falahat

Mám jen kód, protože kód, který byl dosud publikován, se zdá být suboptimální.

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() je přípona GNU. Pokud to nemáte, nebo něco podobného, ​​hodte si vlastní. Například:

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

Osobně bych si hodil vlastní. Můžete použít strtok, ale musíte se o to postarat (zejména pokud odstraňujete vedoucí postavy), abyste věděli, co je to paměť.

Jak se zbavit koncové mezery je snadné, a docela bezpečné, jak můžete jen dát 0 do horní části posledního prostoru, počítat od konce. Jak se zbavit předních prostorů znamená pohybovat se kolem. Chcete-li to udělat na místě (pravděpodobně rozumné), můžete prostě jen posunovat všechno zpět o jednu postavu, dokud nebude žádný vedoucí prostor. Chcete-li být efektivnější, můžete najít index prvního znaku, který není mezerou, a posunout vše zpět o toto číslo. Nebo můžete použít pouze ukazatel na první ne-mezerový znak (ale pak musíte být opatrní stejně jako u strtok).

0
Ben

Trochu pozdě do hry, ale hodím své rutiny do boje. Nejspíš nejde o naprosto nejefektivnější, ale věřím, že jsou správné a jsou jednoduché (s rtrim() posouváním obálky složitosti):

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

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

    Michael Burr
    [email protected]
    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));
}
0
Michael Burr

Jedná se o nejkratší možnou implementaci, na kterou mohu myslet:

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
}
0
Michał Gawlas

Co si myslíte o použití funkce StrTrim definované v záhlaví Shlwapi.h.? Je to přímočaré spíše definování na vlastní pěst. 
Podrobnosti naleznete na adrese: 
http://msdn.Microsoft.com/en-us/library/windows/desktop/bb773454(v=vs.85).aspx

Pokud máte
char ausCaptain[]="GeorgeBailey ";
StrTrim(ausCaptain," ");
To dá ausCaptain jako "GeorgeBailey" ne "GeorgeBailey ".

0
Carthi