मैं मानक तरीके से अग्रणी / अनुगामी व्हाट्सएप को कैसे ट्रिम करूं?


178

क्या सी में एक स्ट्रिंग से अग्रणी और अनुगामी व्हाट्सएप को ट्रिम करने का एक साफ, अधिमानतः मानक तरीका है? मैं अपना रोल करूंगा, लेकिन मुझे लगता है कि यह समान रूप से सामान्य समाधान के साथ एक आम समस्या है।

जवाबों:


164

यदि आप स्ट्रिंग को संशोधित कर सकते हैं:

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

6
क्षमा करें, पहला उत्तर तब तक अच्छा नहीं है जब तक आप मेमोरी लीक की परवाह नहीं करते। अब आपके पास दो ओवरलैपिंग स्ट्रिंग्स हैं (मूल, जिसमें यह अनुगामी रिक्त स्थान है, और नया है)। केवल मूल स्ट्रिंग को मुक्त किया जा सकता है, लेकिन यदि आप करते हैं, तो दूसरा एक मुक्त मेमोरी को इंगित करता है।
डेविड नेहमे

7
@ एनवीएल: कोई मेमोरी आवंटित नहीं की जा रही है, इसलिए मुफ्त में मेमोरी नहीं है।
एडम रोसेनफील्ड

15
@nvl: नहीं str, एक स्थानीय चर है, और इसे बदलने से मूल पॉइंटर को पास नहीं किया जा सकता है। C में फ़ंक्शन कॉल हमेशा पास-पास-मूल्य होते हैं, कभी पास-पास-संदर्भ नहीं होते हैं।
एडम रोसेनफील्ड

11
@ राज: इसमें जो पास किया गया था, उससे अलग पते की वापसी के साथ कुछ भी गलत नहीं है। यहाँ कोई आवश्यकता नहीं है कि लौटाया गया मान free()फ़ंक्शन का वैध तर्क हो । इसके विपरीत - मैंने दक्षता के लिए मेमोरी आवंटन की आवश्यकता से बचने के लिए इसे डिज़ाइन किया है। यदि पते में पारित गतिशील रूप से आवंटित किया गया था, तो कॉल करने वाला अभी भी उस मेमोरी को मुक्त करने के लिए जिम्मेदार है, और कॉलर को यह सुनिश्चित करने की आवश्यकता है कि उस मूल्य को उस मूल्य के साथ अधिलेखित नहीं किया जाए।
एडम रोसेनफील्ड

3
आपको इसके लिए तर्क isspaceदेना होगा unsigned char, अन्यथा आप अपरिभाषित व्यवहार का आह्वान करेंगे।
रोलैंड इलिग सिप १ig

37

यहाँ एक है जो आपके बफर की पहली स्थिति में स्ट्रिंग को स्थानांतरित करता है। आप इस व्यवहार को चाहते हो सकता है कि यदि आपने स्ट्रिंग को गतिशील रूप से आवंटित किया है, तो आप इसे उसी सूचक पर ट्रिम () रिटर्न से मुक्त कर सकते हैं:

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

शुद्धता के लिए परीक्षण:

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

स्रोत फ़ाइल trim.c. 'Cc -Wall trim.c -o trim' के साथ संकलित।


2
आपको इसके लिए तर्क isspaceदेना होगा unsigned char, अन्यथा आप अपरिभाषित व्यवहार का आह्वान करेंगे।
रोलैंड इलिग सिप

@ रोलैंडलीग: धन्यवाद, मैंने कभी महसूस नहीं किया कि यह आवश्यक था। ठीक कर दिया।
indiv

@ सिमास: आप ऐसा क्यों कहते हैं? फ़ंक्शन कॉल करता है isspace()इसलिए " "और के बीच अंतर क्यों होगा "\n"? मैंने नई कहानियों के
indiv

1
@indiv यह मैन्युअल रूप से आवंटित होने पर अमान्य मेमोरी ब्लॉक तक पहुँच प्राप्त करेगा। अर्थात् यह पंक्ति *(endp + 1) = '\0';:। उत्तर पर उदाहरण परीक्षण 64 के एक बफर का उपयोग करता है जो इस समस्या से बचा जाता है।
सिमस

1
@ नोलनड्डा: विस्तार के लिए धन्यवाद। मैंने इसे ठीक कर लिया है और परीक्षण को अद्यतन कर दिया है ताकि मैं बफर से पता लगा सकूं क्योंकि मेरे पास इस समय तक पहुँच नहीं है।
२१:२१

23

मेरा समाधान। स्ट्रिंग परिवर्तनशील होना चाहिए। कुछ अन्य समाधानों के ऊपर लाभ यह है कि यह गैर-स्पेस वाले हिस्से को शुरुआत में ले जाता है ताकि आप पुराने पॉइंटर का उपयोग कर सकते हैं, अगर आपको बाद में इसे मुक्त करना है (तो)।

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 () के लिए _GNU_SOURCE की आवश्यकता होती है, इसलिए हो सकता है कि आपको मॉलॉक () और 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);
}

4
trim()आह्वान यूबी अगर sहै ""के रूप में पहली isspace()कॉल किया जाएगा isspace(p[-1])और p[-1]जरूरी कानूनी स्थान को संदर्भित नहीं करती।
chux - मोनिका

1
आपको इसके लिए तर्क isspaceदेना होगा unsigned char, अन्यथा आप अपरिभाषित व्यवहार का आह्वान करेंगे।
रोलैंड इलिग सिप

1
if(l==0)return;शून्य लंबाई से बचने के लिए जोड़ना चाहिए
ch271828n

11

यहाँ बाएं, दाएं, दोनों, सभी जगह और अलग-अलग ट्रिमिंग के लिए मेरी सी मिनी लाइब्रेरी है, और निर्दिष्ट वर्णों का एक सेट ट्रिमिंग (या डिफ़ॉल्ट रूप से सफेद स्थान)।

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 == dst के स्थान पर ट्रिम होता है , अन्यथा, यह strcpyरूटीन की तरह काम करता है । यह स्ट्रिंग डेलिम में निर्दिष्ट वर्णों के एक सेट को ट्रिम करता हैया सफेद स्थान यदि अशक्त है। यह बाएं, दाएं, दोनों, और सभी (जैसे tr) को ट्रिम करता है। इसके लिए बहुत कुछ नहीं है, और यह केवल एक बार स्ट्रिंग पर पुनरावृत्ति करता है। कुछ लोगों को शिकायत हो सकती है कि दाईं ओर बाईं ओर ट्रिम शुरू होता है, हालांकि, किसी भी स्ट्रलेन की आवश्यकता नहीं है जो वैसे भी बाईं ओर शुरू होता है। (एक रास्ता या दूसरा आपको सही ट्रिम्स के लिए स्ट्रिंग के अंत तक पहुंचना है, इसलिए आप जैसे ही काम करते हैं, वैसे ही कर सकते हैं।) पाइपलाइनिंग और कैश आकार के बारे में तर्क दिए जा सकते हैं और ऐसे - कौन जानता है । चूंकि समाधान बाएं से दाएं काम करता है और केवल एक बार पुनरावृत्त होता है, इसलिए इसे स्ट्रीम पर भी काम करने के लिए विस्तारित किया जा सकता है। सीमाएं: यह यूनिकोड स्ट्रिंग्स पर काम नहीं करता है ।


2
मैंने इसे उकेरा और मुझे इसका पुराना पता है, लेकिन मुझे लगता है कि एक बग है। यह एक सरणी सूचकांक के रूप में उपयोग करने से पहले dtab[*d]कास्ट नहीं करता है । हस्ताक्षरित चार के साथ एक प्रणाली पर यह पढ़ेगा जिससे कीड़े और संभवतः दुर्घटना होगी। *dunsigned intdtab[-127]
ज़ैन लिंक्स

2
संभावित अपरिभाषित व्यवहार dtab[*delim++]क्योंकि charअनुक्रमणिका मानों को डाला जाना चाहिए unsigned char। कोड 8-बिट मानता है chardelimके रूप में घोषित किया जाना चाहिए const char *dtab[0xFF & (unsigned int)*d]के रूप में स्पष्ट होगा dtab[(unsigned char)*d]। कोड UTF-8 एन्कोडेड स्ट्रिंग्स पर काम करता है, लेकिन गैर ASCII रिक्ति क्रमों को स्ट्रिप नहीं करेगा।
चकरली

@ माइकल-प्लेनर, यह दिलचस्प लग रहा है। आप इसका परीक्षण क्यों नहीं करते और इसे GitHub पर डालते हैं?
डेइसुके अरामकी

9

यहाँ एक सरल, अभी तक सही जगह पर ट्रिम समारोह में मेरा प्रयास है।

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
while ((end >= begin) && isspace(str[end]))UB को रोकने के लिए परिवर्तन का सुझाव दें जब str is "" . Prevents str [-1] `।
chux -

Btw, मुझे इसे बदलने के लिए str [i
start

1
आपको इसके लिए तर्क isspaceदेना होगा unsigned char, अन्यथा आप अपरिभाषित व्यवहार का आह्वान करेंगे।
रोलैंड इलिग सिप १ig

@ रोलैंडिग, यह अपरिभाषित व्यवहार क्यों होगा? फ़ंक्शन का उद्देश्य वर्णों के साथ काम करना है।
वोवैनो

@ नहीं, यह नहीं है। इन कार्यों से <ctype.h>अभिप्राय है कि आप unsigned charया तो या विशेष मूल्य का प्रतिनिधित्व करते हैं EOFStackoverflow.com/q/7131026/225757 देखें ।
रोलैंड इलिग

8

ट्रिम पार्टी के लिए देर हो चुकी है

विशेषताएं:
1. अन्य उत्तरों की एक संख्या के रूप में, जल्दी से ट्रिम कर दीजिए।
2. अंत में जाने के बाद, प्रति लूप केवल 1 परीक्षण के साथ सही ट्रिमिंग। @ 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 desired, 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);   // older errant code
    len = (size_t) (p - s + 1);  // Thanks to @theriver
  }

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

3
हां, आखिरकार जो ctype अपरिभाषित व्यवहार के बारे में जानता है।
रोलैंड इलिग

2
@ मुझे लगता है कि इसे लेन = (size_t) (ps) +1 होना चाहिए; अन्यथा अंतिम पत्र ओवरलैप हो जाता है।
थेरेपी

4

यहाँ @ adam-rosenfields के स्थान पर संशोधन दिनचर्या के समान एक समाधान है, लेकिन बिना आवश्यकता के बिना strlen () का सहारा लिया जा रहा है। @Jkramer की तरह, स्ट्रिंग को बफर के भीतर छोड़ दिया गया है ताकि आप उसी पॉइंटर को मुक्त कर सकें। बड़े स्ट्रिंग्स के लिए इष्टतम नहीं है क्योंकि यह मेमोव का उपयोग नहीं करता है। इसमें ++ / - ऑपरेटर शामिल हैं जो @ jfm3 का उल्लेख करते हैं। FCTX आधारित इकाई परीक्षण शामिल थे।

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

यह समाधान सर्वथा खतरनाक है! यदि मूल स्ट्रिंग में कोई भी गैर-व्हाट्सएप वर्ण नहीं है, तो ट्रिम की अंतिम पंक्ति खुशी से अधिलेखित कर देती है जो कुछ भी पूर्व है, यदि उन बाइट्स में 'व्हाट्सएप' बाइट्स होते हैं। अनुकूलन के बिना इसे संकलित करें और देखें कि क्या होता है y: अहस्ताक्षरित x = 0x20202020; char s [4] = ""; अहस्ताक्षरित y = 0x20202020; प्रिंटफ ("& x, & s, & y =% p,% p,% p \ n", & x, & s, & y); प्रिंटफ ("x, [s], y =% 08x, [% s],% 08x \ n", x, s, y); trim_whitespace (रों); प्रिंटफ ("x, [s], y =% 08x, [% s],% 08x \ n", x, s, y);
विलेमोएस

@Villemoes, बग रिपोर्ट के लिए धन्यवाद। मैंने बफर के बाईं ओर चलने से बचने के लिए तर्क को अपडेट किया है जब स्ट्रिंग में केवल व्हाट्सएप होता है। क्या यह नया संस्करण आपकी चिंताओं को दूर करता है?
Rhys Ulerich

भाषा के वकील शायद आपके लिए चिल्लाएंगे कि केवल एक को 'एक' अंक (जो कि आपका '- पी' करेगा) से पहले एक संकेतक बनाने के बारे में अनुमान लगाने के बारे में सोचा था। वास्तविक दुनिया में, आप शायद ठीक हैं। लेकिन आप केवल '> =' से '>' को भी बदल सकते हैं और p के घटने को 'isspace (* - p)' में बदल सकते हैं।
विलेमो

मुझे लगता है कि वकील ठीक होंगे क्योंकि यह केवल एक पते को छूने के बिना तुलना कर रहा है, लेकिन मैं आपके सुझाव को भी कम कर रहा हूं। मैंने उसी के अनुसार इसे अपडेट किया है। धन्यवाद।
Rhys Ulerich

1
doukremt, क्या आपकी चिंता यह है कि फोमोबार के बाद पूरा बफर शून्य से भरा नहीं है? यदि ऐसा है, तो यह काफी अधिक उपयोगी होगा यदि आपने अस्पष्ट चट्टानों को फेंकने के बजाय स्पष्ट रूप से कहा।
Rhys Ulerich

3

एक और एक, असली काम कर एक लाइन के साथ:

#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
स्कैनफ का उपयोग करने के लिए अच्छा विचार; लेकिन उसका केवल एक ही शब्द के साथ काम किया जाएगा जो वह नहीं हो सकता जो ओपी चाहता था (यानी "एबीसी" को ट्रिम करना "एब सी" में परिणामित होना चाहिए, जबकि आपका सिंगल स्कैन "ए" में परिणाम करता है)। इसलिए हमें %nरूपांतरण विनिर्देशक के साथ एक लूप, और स्केप किए गए चार्ट के लिए एक काउंटर की आवश्यकता है, और अंत में इसे हाथ से करना आसान है, मुझे डर है।
पीटर -

बहुत उपयोगी है जब आप स्ट्रिंग का पहला शब्द किसी भी प्रारंभिक स्थानों की उपेक्षा करना चाहते हैं।
जे ... एस

3

मुझे इनमें से अधिकांश उत्तर पसंद नहीं आए क्योंकि उन्होंने निम्नलिखित में से एक या अधिक ...

  1. ओरिजिनल पॉइंटर स्ट्रिंग के अंदर एक अलग पॉइंटर लौटाया (एक ही चीज़ के लिए दो अलग-अलग पॉइंटर्स को टकराने के लिए एक तरह का दर्द)।
  2. स्ट्रैलेन () जैसी चीजों का इस्तेमाल किया जो पूरे स्ट्रिंग को प्री-इट्रेट करता है।
  3. गैर-पोर्टेबल ओएस-विशिष्ट काम करता है।
  4. Backscanned।
  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';
}

2
आपको इसके लिए तर्क isspaceदेना होगा unsigned char, अन्यथा आप अपरिभाषित व्यवहार का आह्वान करेंगे।
रोलैंड इलिग

जैसा कि यह उत्तर "व्यर्थ चक्र" के बारे में चिंतित है, ध्यान दें कि कोड अनावश्यक रूप से पूरे स्टिंग की प्रतिलिपि बनाता है जब कोई स्थान नहीं होता है। एक अग्रणी while (isspace((unsigned char) *szWrite)) szWrite++;को रोक सकता है। कोड सभी अनुगामी सफेद स्थान की भी प्रतिलिपि बनाता है।
chux -

@ यह कार्यान्वयन अलग-अलग रीड एंड राइट पॉइंटर्स के साथ इन-प्लेस को म्यूट करता है (एक अलग स्थान पर एक नया पॉइंटर वापस करने के लिए), इसलिए लाइन-वन पर पहले गैर-स्पेस में szWrite को जंप करने का सुझाव अग्रणी स्थान को छोड़ देगा। मूल स्ट्रिंग।
जेसन स्टीवर्ट

@chux, आप सही हैं कि यह सफ़ेद-स्पेस की नकल करता है (पिछले नॉन-स्पेस कैरेक्टर के बाद नल जोड़ने से पहले), लेकिन यही वह मूल्य है जिसे मैंने स्ट्रिंग को प्री-स्कैनिंग से बचने के लिए चुना था। WS को पीछे करने की मामूली मात्रा के लिए, पिछले गैर-WS char के लिए पूरे स्ट्रिंग को पूर्व-स्कैन करने के बजाय बस बाइट्स की प्रतिलिपि बनाना सस्ता है। WS को अनुगामी बनाने के लिए बड़ी मात्रा में, पूर्व-स्कैनिंग संभवत: लिखने में कमी के लायक होगी।
जेसन स्टीवर्ट

@chux, "प्रतियां जब कोई स्थान नहीं है" स्थिति के लिए, केवल *szWrite = *szReadतब जब प्रदर्शनकर्ता समान नहीं होते हैं, उस मामले में लिखना छोड़ देंगे, लेकिन फिर हमने एक और तुलना / शाखा जोड़ी है। आधुनिक सीपीयू / एमएमयू / बीपी के साथ, मुझे नहीं पता है कि चेक नुकसान या लाभ होगा। सरल प्रोसेसर और मेमोरी आर्किटेक्चर के साथ, यह सिर्फ कॉपी करना और तुलना छोड़ना सस्ता है।
जेसन स्टीवर्ट

2

पार्टी को बहुत देर ...

बिना पास के सिंगल-फॉरवर्ड-स्कैनिंग समाधान। स्रोत स्ट्रिंग में प्रत्येक वर्ण को दो बार ठीक एक बार जांचा जाता है । (इसलिए यह अन्य समाधानों की तुलना में सबसे अधिक तेजी से होना चाहिए, खासकर अगर स्रोत स्ट्रिंग में बहुत अधिक जगह है।)

इसमें दो समाधान शामिल हैं, एक स्रोत स्ट्रिंग को दूसरे गंतव्य स्ट्रिंग में कॉपी और ट्रिम करने के लिए, और दूसरा स्रोत स्ट्रिंग को जगह में ट्रिम करने के लिए। दोनों फ़ंक्शन समान कोड का उपयोग करते हैं।

(परिवर्तनीय) स्ट्रिंग को अंदर ले जाया जाता है, इसलिए इसका मूल सूचक अपरिवर्तित रहता है।

#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
स्रोत स्ट्रिंग के प्रत्येक वर्ण का एक बार ठीक से परीक्षण किया जाता है : वास्तव में नहीं, स्रोत स्ट्रिंग के अधिकांश वर्णों का दो बार परीक्षण किया जाता है: की तुलना में '\0'और फिर परीक्षण किया जाता है isspace()। इसके साथ सभी पात्रों का परीक्षण करना बेकार लगता है isspace()। स्ट्रिंग के अंत से बैकट्रैकिंग गैर रोग संबंधी मामलों के लिए अधिक कुशल होनी चाहिए।
चकरली

@chqrlie - हां, प्रत्येक चरित्र का दो बार परीक्षण किया जाता है। मैं वास्तव में परीक्षण किए गए इस कोड को देखना चाहता हूं, विशेष रूप से बहुत सारे अनुगामी स्थानों के साथ तार दिए गए हैं, जैसा कि यहां अन्य एल्गोरिदम की तुलना में है।
डेविड आर ट्रिब्बल

trim()ठीक है। कॉर्नर केस: ओवरलैप और trim2(char *d, const char *s)जब परेशानी होती d,sहै s < d
chux -

@chux - उस कोने के मामले में, कैसे trim()व्यवहार करना चाहिए ? आप स्ट्रिंग द्वारा स्वयं द्वारा अधिग्रहित मेमोरी में एक स्ट्रिंग को ट्रिम और कॉपी करने के लिए कह रहे हैं। इसके विपरीत memmove(), इसे ट्रिम करने से पहले स्रोत स्ट्रिंग की लंबाई निर्धारित करने की आवश्यकता होती है, जिससे पूरे स्ट्रिंग को अतिरिक्त समय स्कैन करने की आवश्यकता होती है। एक अलग rtrim2()फ़ंक्शन लिखने के लिए बेहतर है जो स्रोत को गंतव्य की ओर पीछे कॉपी करना जानता है, और शायद एक अतिरिक्त स्रोत स्ट्रिंग लंबाई तर्क लेता है।
डेविड आर ट्रिब्बल

1

मुझे यकीन नहीं है कि आप क्या "दर्द रहित" मानते हैं।

सी तार बहुत दर्दनाक हैं। हम पहले गैर-व्हाट्सएप चरित्र स्थिति को तुच्छ रूप से पा सकते हैं:

जबकि (isspace (* p)) p ++;

हम दो समान तुच्छ चाल के साथ अंतिम गैर-व्हाट्सएप चरित्र स्थिति पा सकते हैं:

जबकि (* q) q ++;
करना {q--; } जबकि (isspace (* q));

(मैंने एक ही समय में आपको *और ++ऑपरेटरों का उपयोग करने का दर्द बख्शा है ।)

अब सवाल यह है कि आप इसके साथ क्या करते हैं? हाथ में डेटाटाइप वास्तव में एक बड़ा मजबूत सार नहीं Stringहै, जिसके बारे में सोचना आसान है, लेकिन इसके बजाय वास्तव में भंडारण बाइट्स की एक सरणी से अधिक मुश्किल है। एक मजबूत डेटा प्रकार को खोना, एक फ़ंक्शन लिखना असंभव है जो PHperytonby के chompफ़ंक्शन के समान होगा । सी रिटर्न में ऐसा क्या काम करेगा?


यह अच्छी तरह से काम करता है जब तक कि स्ट्रिंग सभी सफेद-रिक्त स्थान से बना न हो। इससे पहले एक बार जाँच की आवश्यकता है do { q--; } ...पता करने के लिए *q != 0
chux - मोनिका

1

उदाहरण के लिए, स्ट्रिंग लाइब्रेरी का उपयोग करें :

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

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

... जैसा कि आप कहते हैं कि यह एक "आम" समस्या है, हाँ आपको एक # शामिल करने की आवश्यकता है या तो और यह लिबक में शामिल नहीं है, लेकिन यादृच्छिक बिंदुओं को संग्रहीत करने के लिए अपनी खुद की हैक जॉब का आविष्कार न करें और size_t उस तरह से ही आगे बढ़ता है बफर ओवरफ्लो।



1

बस इस बढ़ते रहने के लिए, एक परिवर्तनीय स्ट्रिंग के साथ एक और विकल्प:

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_tजो की सीमा को पार कर सकता है int। श्वेत स्थान अंतरिक्ष वर्ण तक सीमित नहीं है। अंत में लेकिन सबसे महत्वपूर्ण: strcpy(string, string + i * sizeof(char));स्रोत और गंतव्य सरणियों के ओवरलैप होने पर अपरिभाषित व्यवहार । के memmove()बजाय का उपयोग करें strcpy()
चकरली

@chqrlie आप सही हैं, बस अपने सुझावों को शामिल करें। मैं समझता हूं कि जब स्रोत और गंतव्य ओवरलैप के कारण प्रतिलिपि व्यवहार अपरिभाषित व्यवहार का कारण बन सकता है, लेकिन सिर्फ यह बताना चाहता हूं कि इस विशेष मामले में यह कोई समस्या नहीं होनी चाहिए क्योंकि हम हमेशा स्मृति के बाद की स्थिति से शुरुआत तक कॉपी करने जा रहे हैं, प्रतिक्रिया के लिए धन्यवाद।
wallek876

1
इससे कोई फर्क नहीं पड़ता कि स्रोत और गंतव्य सरणियाँ कैसे ओवरलैप होती हैं, यह अपरिभाषित व्यवहार है। बढ़ते हुए पतों के साथ एक बार में एक बाइट लगने वाली धारणा पर भरोसा न करें। इसके अलावा मैं यह उल्लेख करना भूल गया कि while (isspace((int)string[i])) string[i--] = '\0';स्ट्रिंग की शुरुआत से परे लूप हो सकता है। आपको इस लूप को पिछली और निम्न पंक्तियों के साथ जोड़ना चाहिए और लिखना होगाwhile (i > 0 && isspace((unsigned char)string[--i])) { string[i] = '\0'; } size_t end = i;
chqrlie

@chqrlie अच्छा बिंदु, सभी सफेद रिक्त स्थान के साथ एक स्ट्रिंग शुरुआत में पाश के कारण होता है, ऐसा नहीं सोचा था।
wallek876

वास्तव में, मेरा सुझाव गलत endथा क्योंकि पीछे की ओर लटकती अशक्त बाइट की ओर इशारा नहीं किया गया था और आपके पास end = ++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;
}

2
नोट: isspace(*str)यूबी जब *str < 0
chux -

1
का उपयोग size_t nअच्छा है, फिर भी इंटरफ़ेस nपूरी तरह से छंटनी की स्ट्रिंग के लिए बहुत छोटा होने के बारे में किसी भी तरह से कॉल करने वाले को सूचित नहीं करता है । विचार करेंtrim(out, 12, "delete data not")
chux -

1

एक स्ट्रिंग में अग्रणी स्थानों को छोड़ने का सबसे आसान तरीका है, imho,

#include <stdio.h>

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

1
यह बीच में रिक्त स्थान के साथ तार के लिए काम नहीं करेगा, जैसे कि " foo bar "
डेविड आर ट्राइबल

1

ठीक है यह मेरा सवाल है। मेरा मानना ​​है कि यह सबसे संक्षिप्त समाधान है जो जगह में स्ट्रिंग को संशोधित करता है ( freeकाम करेगा) और किसी भी यूबी से बचा जाता है। छोटे तार के लिए, यह शायद मेम्मोव से जुड़े समाधान की तुलना में तेज़ है।

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 > strपरीक्षण केवल एक बार की जरूरत है। *b = 0;केवल एक बार की जरूरत है।
chux -

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 सभी सफेद रिक्त स्थान को ट्रिम करने में मदद करता है।

  • अंतरिक्ष चरित्र के लिए अंतिम बाइट से जांच करने और लंबाई चर को कम करने के लिए पहला लूप चलाएं
  • अंतरिक्ष चरित्र के लिए पहली बाइट से जांच करने के लिए एक दूसरा लूप चलाएं और लंबाई चर और वेतन वृद्धि सूचक को कम करें।
  • अंत में यदि लंबाई चर 0 से अधिक है, तो strndupरिक्त स्थान को छोड़कर नए स्ट्रिंग बफर बनाने के लिए उपयोग करें।

बस थोड़ा सा नाइटपिक, strndup()सी मानक का हिस्सा नहीं है , लेकिन केवल पॉज़िक्स है। लेकिन इसे लागू करना काफी आसान है क्योंकि यह कोई बड़ी बात नहीं है।
पैट्रिक श्ल्टर

trim_space("")लौटता है NULL। मुझे एक पॉइंटर की उम्मीद है ""int len;होना चाहिए size_t len;isspace(in[len - 1])यूबी जब in[len - 1] < 0
chux -

while (isspace((unsigned char) *in) in++;पहले एक प्रारंभिक len = strlen(in);बाद की तुलना में अधिक कुशल होगाwhile(len && *in && isspace(*in)) ++in, --len;
चक्स -

0

व्यक्तिगत रूप से, मैं अपना रोल करूंगा। आप strtok का उपयोग कर सकते हैं, लेकिन आपको ऐसा करने के साथ ध्यान रखने की ज़रूरत है (विशेषकर यदि आप अग्रणी पात्रों को हटा रहे हैं) तो आपको पता है कि स्मृति क्या है।

ट्रेलिंग रिक्त स्थान से छुटकारा पाना आसान है, और बहुत सुरक्षित है, क्योंकि आप अंतिम स्थान के शीर्ष पर एक 0 डाल सकते हैं, अंत से वापस गिन सकते हैं। अग्रणी स्थानों से छुटकारा पाने का मतलब है कि चीजों को इधर-उधर करना। यदि आप इसे (संभवत: समझदार) करना चाहते हैं, तो आप केवल एक चरित्र को वापस ले जा सकते हैं जब तक कि कोई अग्रणी स्थान न हो। या, अधिक कुशल होने के लिए, आप पहले गैर-अंतरिक्ष वर्ण का सूचकांक पा सकते हैं, और उस नंबर से सब कुछ वापस स्थानांतरित कर सकते हैं। या, आप पहले नॉन-स्पेस कैरेक्टर के लिए एक पॉइंटर का इस्तेमाल कर सकते हैं (लेकिन फिर आपको उसी तरह से सावधान रहने की जरूरत है जैसे आप स्ट्रेटोक के साथ करते हैं)।


4
स्ट्रेटोक आमतौर पर उपयोग करने के लिए बहुत अच्छा उपकरण नहीं है - कम से कम नहीं क्योंकि यह फिर से प्रवेश नहीं है। यदि आप किसी एकल फ़ंक्शन के अंदर रहते हैं, तो इसका उपयोग सुरक्षित रूप से किया जा सकता है, लेकिन यदि थ्रेड्स या अन्य कार्यों को कॉल करने की कोई संभावना है जो स्वयं स्ट्रैटोक का उपयोग कर सकते हैं, तो आप मुसीबत में हैं।
जोनाथन लेफ़लर

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
इससे मुझे हंसी आई क्योंकि मुझे लगा कि ड्रीमलैक्स ने टेस्ट स्ट्रिंग को "बड़े समय तक बेकार" शामिल करने के लिए संपादित किया है। नहीं। मूल लेखक सिर्फ ईमानदार है।
जेम्स मॉरिस

1
इस कोड का उपयोग न करें। यह एक बफर अतिप्रवाह पैदा करता है।
रोलैंड इलिग सिप १ig

0

खेल में थोड़ा देर हो गई, लेकिन मैं अपनी दिनचर्या को मैदान में उतार दूंगा। वे शायद सबसे पूर्ण कुशल नहीं हैं, लेकिन मेरा मानना ​​है कि वे सही हैं और वे सरल हैं ( 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
संभावित नकारात्मक मानों पर अपरिभाषित व्यवहार से बचने के लिए आपको charतर्क देना चाहिए । यदि आवश्यक न हो तो स्ट्रिंग को स्थानांतरित करने से भी बचें । isspace()(unsigned char)ltrim()
चकरली

0

अब तक के अधिकांश उत्तर निम्नलिखित में से एक करते हैं:

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

1
यदि आप प्रदर्शन से चिंतित हैं, तो उपयोग न करें strspn()और strcspn()एक तंग लूप में। यह बहुत अक्षम है और ओवरहेड सिंगल फॉरवर्ड पास के अप्रमाणित लाभ को बौना कर देगा। strlen()आमतौर पर बहुत कुशल कोड के साथ इनलाइन का विस्तार किया जाता है, वास्तविक चिंता का विषय नहीं है। स्ट्रिंग की शुरुआत और अंत ट्रिमिंग बहुत कम या कोई गैर-सफेद अक्षरों के साथ स्ट्रिंग के विशेष मामले में भी सफेदी के लिए स्ट्रिंग में हर चरित्र का परीक्षण करने की तुलना में बहुत तेज होगी।
चकरली

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
}

1
इस बारे में कैसे: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

ये फ़ंक्शन मूल बफर को संशोधित करेंगे, इसलिए यदि गतिशील रूप से आवंटित किया गया है, तो मूल सूचक को मुक्त किया जा सकता है।

#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()खाली स्ट्रिंग पर अपरिभाषित व्यवहार करता है। lstrip()व्हाईटस्पेस पात्रों के एक लंबे प्रारंभिक भाग के साथ स्ट्रिंग पर अनावश्यक रूप से धीमा है। isspace()एक charतर्क पारित नहीं किया जाना चाहिए क्योंकि यह नकारात्मक मूल्यों पर अपरिभाषित व्यवहार को अलग से आमंत्रित करता है EOF
13

0

क्या आप शीर्ष लेख Shlwapi.h में परिभाषित StrTrim फ़ंक्शन का उपयोग करने के बारे में सोचते हैं? यह अपने दम पर परिभाषित करने के बजाय सीधे आगे है।
विवरण यहाँ पर पाया जा सकता है:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773454(v=vs.85).aspx

यदि आपके पास
char ausCaptain[]="GeorgeBailey ";
StrTrim(ausCaptain," ");
यह है तो नहीं के ausCaptainरूप में दे देंगे ।"GeorgeBailey""GeorgeBailey "


0

अपने तार को दोनों तरफ से ट्रिम करने के लिए मैं पुराने का उपयोग करता हूं, लेकिन गॉडी;) यह एक जगह से कम इस्की के साथ कुछ भी ट्रिम कर सकता है, जिसका अर्थ है कि नियंत्रण चार्ट भी छंटनी किए जाएंगे!

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

आपको size_tइसके बजाय उपयोग करना चाहिए unsigned int। कोड में बहुत सारे अनावश्यक परीक्षण हैं और अपरिभाषित व्यवहार को आमंत्रित करता है strncpy(strData,&strData[S],L)क्योंकि स्रोत और गंतव्य सरणियाँ ओवरलैप होती हैं। के memmove()बजाय का उपयोग करें strncpy()
चकरली

इस मामले में यह ठीक है क्योंकि गंतव्य पते में हमेशा स्रोत की तुलना में छोटे सूचकांक होते हैं, लेकिन हां मेमोव वास्तव में बेहतर होगा।
'Добромиров'

नहीं, यह ठीक नहीं है। इससे कोई फर्क नहीं पड़ता कि स्रोत और गंतव्य कैसे ओवरलैप होते हैं, यह अपरिभाषित व्यवहार को आमंत्रित करता है क्योंकि आप अपने मानक विनिर्देश से परे पुस्तकालय कार्यों के कार्यान्वयन पर सुरक्षित रूप से धारणा नहीं बना सकते हैं। आधुनिक कंपाइलर संभावित अपरिभाषित व्यवहार के साथ स्थितियों का अनुचित लाभ उठाते हैं, इसे सुरक्षित खेलते हैं और यूबी से दूर रहते हैं, और नौसिखिया को असुरक्षित धारणा नहीं बनने देते हैं।
chqrlie

0

मैं केवल कोड शामिल कर रहा हूं क्योंकि अब तक पोस्ट किया गया कोड सबॉप्टिमल लगता है (और मेरे पास अभी तक टिप्पणी करने के लिए प्रतिनिधि नहीं है।)

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()एक GNU एक्सटेंशन है। यदि आपके पास यह या कुछ समतुल्य नहीं है, तो अपना स्वयं का रोल करें। उदाहरण के लिए:

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

1
isspace(0)को गलत माना जाता है, आप दोनों कार्यों को सरल बना सकते हैं। इसके अलावा ब्लॉक के memmove()अंदर ले जाएँ if
चकरली

0

यहाँ मैं डायनेमिक मेमोरी एलोकेशन का उपयोग करके इनपुट स्ट्रिंग को ट्रिमस्ट्रीम में ट्रिम कर देता हूँ। सबसे पहले, हम पाते हैं कि इनपुट स्ट्रिंग में कितने गैर-खाली वर्ण मौजूद हैं। फिर, हम उस आकार के साथ एक वर्ण सरणी आवंटित करते हैं और अशक्त समाप्त वर्ण की देखभाल करते हैं। जब हम इस फ़ंक्शन का उपयोग करते हैं, तो हमें मुख्य फ़ंक्शन के अंदर मेमोरी को मुक्त करना होगा।

#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

यहाँ है कि मैं यह कैसे करते हैं। यह स्ट्रिंग को जगह में बदल देता है, इसलिए लौटे हुए स्ट्रिंग से निपटने या आवंटित स्ट्रिंग को पॉइंटर खो देने के बारे में कोई चिंता नहीं है। यह सबसे कम उत्तर संभव नहीं हो सकता है, लेकिन अधिकांश पाठकों के लिए यह स्पष्ट होना चाहिए।

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

पाठकों के लिए बिल्कुल स्पष्ट है, लेकिन strlen एक और पाश प्रदर्शन करता है .. :)
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
हालांकि यह कोड प्रश्न का उत्तर दे सकता है, लेकिन यह समस्या का हल कैसे और / या इसके बारे में अतिरिक्त संदर्भ प्रदान करता है, इससे उत्तर के दीर्घकालिक मूल्य में सुधार होगा।
निक 3500
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.