Yanıtlar:
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;
}
str
yerel 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.
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.
isspace
gerekir unsigned char
, aksi takdirde tanımlanmamış davranışları çağırırsınız.
İş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.
isspace
gerekir unsigned char
, aksi takdirde tanımlanmamış davranışları çağırırsınız.
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
*(endp + 1) = '\0';
. Yanıttaki örnek testte, bu sorunu önleyen 64'lük bir tampon kullanılır.
Çö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);
}
trim()
çağıran UB eğer s
olduğunu ""
ilk olarak isspace()
çağrı olacağını isspace(p[-1])
ve p[-1]
mutlaka yasal bir konuma başvuruda bulunmuyor.
isspace
gerekir unsigned char
, aksi takdirde tanımlanmamış davranışları çağırırsınız.
if(l==0)return;
uzunlukta str önlemek için eklemeniz gerekir
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.
#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
#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 strcpy
rutinler 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
dtab[*d]
döküm gelmez *d
için unsigned int
bir dizi endeks olarak kullanmadan önce. İmzalı karakter bulunan bir sistemde bu, dtab[-127]
hatalara ve muhtemelen çökmelere neden olacak şekilde okunacaktır .
dtab[*delim++]
nedeniyle char
endeks değerleri dökülmesi gerekir unsigned char
. Kod 8 bit olduğunu varsayar char
. delim
olarak 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.
İş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.
}
while ((end >= begin) && isspace(str[end]))
olduğunda UB'yi önlemek için olarak değiştirmenizi öneririz . str is
. Prevents
isspace
gerekir unsigned char
, aksi takdirde tanımlanmamış davranışları çağırırsınız.
<ctype.h>
temsil eden ints ile çalışması amaçlanmıştır . Bkz. Stackoverflow.com/q/7131026/225757 . unsigned char
EOF
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 char
bir imzalanır char
, döküm *s
için unsigned char
.
Karakter işleme "Her durumda argüman bir
int
değeridirunsigned char
, değeri makrosu ile temsil edilebilir veya makronun değerine eşit olacaktırEOF
. 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);
}
İş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();
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);
}
%n
dönüşüm belirteciyle atlanan grafikler için bir sayaca ihtiyacımız var ve sonunda bunu elle yapmak daha basit, korkarım.
Bu cevapların çoğunu beğenmedim çünkü aşağıdakilerden bir veya daha fazlasını yaptılar ...
İş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';
}
isspace
gerekir unsigned char
, aksi takdirde tanımlanmamış davranışları çağırırsınız.
while (isspace((unsigned char) *szWrite)) szWrite++;
bunu engelleyecektir. Kod ayrıca tüm boşlukları kopyalar.
*szWrite = *szRead
iş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.
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);
}
'\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.
trim()
TAMAM. Köşe vaka: trim2(char *d, const char *s)
sorun vardır d,s
örtüşme ve s < d
.
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.
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 String
düşü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 chomp
işleviyle aynı işlevi yapacak bir işlev yazmak imkansızdır . C'de böyle bir işlev ne döndürür?
do { q--; } ...
etmeniz gerekiyor *q != 0
.
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ı.
Kullanıyorsanız glib
, o zaman g_strstrip kullanabilirsiniz
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);
}
strlen()
size_t
aralığı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()
.
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;
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.
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;
}
isspace(*str)
Ne zaman *str < 0
.
size_t n
iyidir, ancak arabirim, n
kesilmiş bir dize için çok küçük olduğunda arayanı hiçbir şekilde bilgilendirmez . Düşününtrim(out, 12, "delete data not")
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;
}
" foo bar "
.
Tamam bu soruyu benim üstlenmem. Ben yerinde dize değiştirir ( free
iş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 > str
Test sadece bir kez gereklidir. *b = 0;
sadece bir kez gerekli.
#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.
strndup
boşlukları hariç tutarak yeni dize arabelleği oluşturmak için kullanın .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.
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
.
while (isspace((unsigned char) *in) in++;
önce bir başlangıç len = strlen(in);
daha sonradan daha verimli olurduwhile(len && *in && isspace(*in)) ++in, --len;
Ş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).
#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;
}
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));
}
char
etmek 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 .
Şimdiye kadar cevapların çoğu aşağıdakilerden birini yapıyor:
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';
}
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.
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
}
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; }
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 char
argüman geçirilmemelidir, çünkü negatif değerlerde farklı tanımlanmamış davranışlar başlatır EOF
.
Shlwapi.h üstbilgisinde tanımlanan StrTrim işlevini kullanma hakkında ne düşünüyorsunuz? Kendi başınıza tanımlamaktan ziyade doğrudan ileriye doğru.
Ayrıntılar şu adreste bulunabilir:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773454(v=vs.85).aspx
Eğer varsa
char ausCaptain[]="GeorgeBailey ";
StrTrim(ausCaptain," ");
bu verecektir ausCaptain
olarak "GeorgeBailey"
değil "GeorgeBailey "
.
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;
}
size_t
yerine 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()
.
Ş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';
isspace(0)
yanlış olarak tanımlanır, her iki işlevi de basitleştirebilirsiniz. Ayrıca bloğun memmove()
içini hareket ettirin if
.
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;
}
İş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';
}
}
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;
}