Jawaban:
Jika Anda dapat memodifikasi string:
// 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;
}
Jika Anda tidak dapat mengubah string, pada dasarnya Anda dapat menggunakan metode yang sama:
// 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
adalah variabel lokal, dan mengubahnya tidak mengubah pointer asli yang dilewatkan. Panggilan fungsi dalam C selalu pass-by-value, tidak pernah pass-by-reference.
free()
fungsi yang valid . Justru sebaliknya - saya merancang ini untuk menghindari kebutuhan alokasi memori untuk efisiensi. Jika alamat yang diteruskan dialokasikan secara dinamis, maka penelepon masih bertanggung jawab untuk membebaskan memori itu, dan penelepon perlu memastikan untuk tidak menimpa nilai itu dengan nilai yang dikembalikan di sini.
isspace
untuk unsigned char
, jika Anda menjalankan perilaku undefined.
Inilah salah satu yang menggeser string ke posisi pertama buffer Anda. Anda mungkin menginginkan perilaku ini sehingga jika Anda mengalokasikan string secara dinamis, Anda masih bisa membebaskannya pada pointer yang sama yang memangkas () mengembalikan:
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;
}
Uji kebenaran:
#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;
}
File sumber tadinya trim.c. Dikompilasi dengan 'cc -Wall trim.c -o trim'.
isspace
untuk unsigned char
, jika Anda menjalankan perilaku undefined.
isspace()
jadi mengapa ada perbedaan antara " "
dan "\n"
? Saya menambahkan unit test untuk baris baru dan terlihat OK dengan saya ... ideone.com/bbVmqo
*(endp + 1) = '\0';
. Contoh pengujian pada jawaban menggunakan buffer 64 yang menghindari masalah ini.
Solusi saya. String harus dapat diubah. Keuntungan di atas beberapa solusi lain yang memindahkan bagian non-ruang ke awal sehingga Anda dapat tetap menggunakan pointer lama, jika Anda harus membebaskan () nanti.
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);
}
Versi ini membuat salinan string dengan strndup () alih-alih mengeditnya di tempat. strndup () membutuhkan _GNU_SOURCE, jadi mungkin Anda perlu membuat strndup sendiri () dengan malloc () dan 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);
}
trim()
memanggil UB jika s
adalah ""
yang pertama isspace()
panggilan akan isspace(p[-1])
dan p[-1]
tidak selalu referensi lokasi hukum.
isspace
untuk unsigned char
, jika Anda menjalankan perilaku undefined.
if(l==0)return;
untuk menghindari str nol panjang
Inilah perpustakaan mini C saya untuk memotong kiri, kanan, keduanya, semua, di tempat dan terpisah, dan memotong seperangkat karakter yang ditentukan (atau spasi putih secara default).
#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); }
Satu rutinitas utama melakukan semuanya. Ini terpotong pada tempatnya jika src == dst , jika tidak, itu berfungsi seperti strcpy
rutinitas. Ini memangkas satu set karakter yang ditentukan dalam batasan string , atau spasi putih jika nol. Ini memotong kiri, kanan, keduanya, dan semua (seperti tr). Tidak ada banyak untuk itu, dan itu berulang di atas string hanya sekali. Beberapa orang mungkin mengeluh bahwa trim kanan mulai di sebelah kiri, namun, tidak ada strlen yang diperlukan yang dimulai dari kiri. (Dengan satu atau lain cara, Anda harus sampai ke akhir string untuk trim yang tepat, jadi sebaiknya Anda melakukan pekerjaan sambil jalan.) Mungkin ada argumen yang dibuat tentang pipelining dan ukuran cache dan semacamnya - siapa tahu . Karena solusinya bekerja dari kiri ke kanan dan hanya mengulangi sekali saja, solusinya dapat diperluas untuk bekerja pada stream juga. Keterbatasan: ini tidak bekerja pada unicode string.
dtab[*d]
tidak dilempar *d
ke unsigned int
sebelum menggunakannya sebagai indeks array. Pada sistem dengan char yang ditandatangani ini akan membaca dtab[-127]
yang akan menyebabkan bug dan mungkin crash.
dtab[*delim++]
karena char
nilai indeks harus dilemparkan ke unsigned char
. Kode mengasumsikan 8-bit char
. delim
harus dinyatakan sebagai const char *
. dtab[0xFF & (unsigned int)*d]
akan lebih jelas sebagai dtab[(unsigned char)*d]
. Kode ini bekerja pada string yang dikodekan UTF-8, tetapi tidak akan menghapus urutan spasi ASCII non.
Berikut ini adalah upaya saya pada fungsi trim in-place yang sederhana namun benar.
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]))
mencegah UB ketika str is
"" . Prevents
str [-1] `.
isspace
untuk unsigned char
, jika Anda menjalankan perilaku undefined.
<ctype.h>
dimaksudkan untuk bekerja dengan int, yang mewakili unsigned char
nilai khusus atau EOF
. Lihat stackoverflow.com/q/7131026/225757 .
Terlambat ke pesta trim
Fitur:
1. Pangkas awal dengan cepat, seperti pada sejumlah jawaban lainnya.
2. Setelah pergi ke akhir, memotong kanan dengan hanya 1 tes per loop. Seperti @ jfm3, tetapi berfungsi untuk semua string spasi putih)
3. Untuk menghindari perilaku yang tidak terdefinisi saat char
ditandatangani char
, masukkan *s
ke unsigned char
.
Penanganan karakter "Dalam semua kasus, argumennya adalah
int
, nilainya harus dinyatakan sebagaiunsigned char
atau sama dengan nilai makroEOF
. Jika argumen memiliki nilai lain, perilaku tidak terdefinisi." 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 berkomentar di atas tidak menggeser string yang dipangkas. Untuk melakukannya ....
// 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);
}
Berikut adalah solusi yang mirip dengan rutin modifikasi in-place @ adam-rosenfields tetapi tanpa harus menggunakan strlen (). Seperti @jkramer, string disetel ke kiri di dalam buffer sehingga Anda dapat membebaskan pointer yang sama. Tidak optimal untuk string besar karena tidak menggunakan memmove. Termasuk operator ++ / - yang @jfm3 sebutkan. Termasuk unit test berbasis 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();
Satu lagi, dengan satu baris melakukan pekerjaan nyata:
#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
specifier konversi, dan pada akhirnya itu hanya lebih mudah untuk melakukannya dengan tangan, saya khawatir.
Saya tidak suka sebagian besar jawaban ini karena mereka melakukan satu atau lebih dari yang berikut ...
Ini versi saya:
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
untuk unsigned char
, jika Anda menjalankan perilaku undefined.
while (isspace((unsigned char) *szWrite)) szWrite++;
akan mencegah hal itu. Code juga menyalin semua spasi putih yang tertinggal.
*szWrite = *szRead
ketika pointer tidak sama akan melewatkan penulisan dalam kasus itu, tapi kemudian kami telah menambahkan perbandingan / cabang lain. Dengan CPU / MMU / BP modern, saya tidak tahu apakah cek itu akan menjadi kerugian atau keuntungan. Dengan prosesor dan arsitektur memori yang lebih sederhana, lebih murah untuk hanya menyalin dan melewati perbandingan.
Sangat terlambat ke pesta ...
Solusi pemindaian lintasan tunggal tanpa backtracking. Setiap karakter dalam string sumber diuji tepat dua kali sekali . (Jadi itu harus lebih cepat daripada sebagian besar solusi lain di sini, terutama jika string sumber memiliki banyak ruang tambahan.)
Ini termasuk dua solusi, satu untuk menyalin dan memotong string sumber ke string tujuan lain, dan yang lainnya untuk memotong string sumber di tempatnya. Kedua fungsi menggunakan kode yang sama.
String (yang dapat dimodifikasi) dipindahkan di tempat, jadi penunjuk asli tetap tidak berubah.
#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'
dan kemudian diuji dengan isspace()
. Tampaknya boros untuk menguji semua karakter isspace()
. Mundur dari ujung tali harus lebih efisien untuk kasus non patologis.
trim()
BAIK. Kasus sudut: trim2(char *d, const char *s)
memiliki masalah saat d,s
tumpang tindih dan s < d
.
trim()
bersikap? Anda meminta untuk memotong dan menyalin string ke memori yang ditempati oleh string itu sendiri. Tidak seperti memmove()
ini, ini membutuhkan penentuan panjang string sumber sebelum melakukan trim itu sendiri, yang membutuhkan pemindaian seluruh string tambahan waktu. Lebih baik menulis rtrim2()
fungsi berbeda yang tahu untuk menyalin sumber ke tujuan mundur, dan mungkin membutuhkan argumen panjang string sumber tambahan.
Saya tidak yakin apa yang Anda anggap "tidak menyakitkan."
Senar C sangat menyakitkan. Kita dapat menemukan posisi karakter non-spasi putih pertama secara sepele:
while (isspace (* p)) p ++;
Kita dapat menemukan posisi karakter non-spasi putih terakhir dengan dua gerakan sepele yang serupa:
sementara (* q) q ++; lakukan {q--; } while (isspace (* q));
(Saya telah menyelamatkan Anda dari rasa sakit menggunakan *
dan ++
operator pada saat yang sama.)
Pertanyaannya sekarang adalah apa yang Anda lakukan dengan ini? Datatype yang ada saat ini sebenarnya bukan abstrak String
yang kuat dan besar yang mudah dipikirkan, tetapi sebenarnya hampir tidak lebih dari sebuah array byte penyimpanan. Karena tidak memiliki tipe data yang kuat, tidak mungkin untuk menulis fungsi yang akan melakukan hal yang sama seperti chomp
fungsi PHperytonby . Apa fungsi seperti itu di C kembali?
do { q--; } ...
tahu *q != 0
.
Gunakan pustaka string , misalnya:
Ustr *s1 = USTR1(\7, " 12345 ");
ustr_sc_trim_cstr(&s1, " ");
assert(ustr_cmp_cstr_eq(s1, "12345"));
... seperti yang Anda katakan ini adalah masalah "umum", ya Anda harus menyertakan #include atau lebih dan itu tidak termasuk dalam libc tapi jangan menciptakan pekerjaan hack Anda sendiri menyimpan pointer acak dan size_t dengan cara itu hanya mengarah ke buffer overflows.
Jika Anda menggunakan glib
, maka Anda dapat menggunakan g_strstrip
Untuk menjaga pertumbuhan ini, satu opsi lagi dengan string yang dapat dimodifikasi:
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()
mengembalikan a size_t
yang dapat melebihi kisaran int
. ruang putih tidak terbatas pada karakter ruang. Akhirnya tetapi yang paling penting: Perilaku tidak terdefinisi pada strcpy(string, string + i * sizeof(char));
karena array sumber dan tujuan tumpang tindih. Gunakan memmove()
sebagai ganti strcpy()
.
while (isspace((int)string[i])) string[i--] = '\0';
mungkin loop melampaui awal string. Anda harus menggabungkan loop ini dengan baris sebelumnya dan berikut dan tuliswhile (i > 0 && isspace((unsigned char)string[--i])) { string[i] = '\0'; } size_t end = i;
end
tidak menunjuk ke null byte yang tertinggal dan Anda end = ++i;
masih memiliki masalah untuk string yang berisi semua karakter spasi putih. Saya baru saja memperbaiki kodenya.
Saya tahu ada banyak jawaban, tetapi saya mengirim jawaban saya di sini untuk melihat apakah solusi saya cukup baik.
// 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)
UB kapan *str < 0
.
size_t n
bagus, namun antarmuka tidak memberi tahu pemanggil dengan cara apa pun ketika n
terlalu kecil untuk string yang dipangkas sepenuhnya. Pertimbangkantrim(out, 12, "delete data not")
Cara termudah untuk melewati spasi dalam string adalah, 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 "
.
Ok, inilah pendapat saya. Saya percaya ini adalah solusi paling ringkas yang memodifikasi string di tempat ( free
akan bekerja) dan menghindari UB. Untuk string kecil, itu mungkin lebih cepat daripada solusi yang melibatkan 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;
}
b > str
tes hanya dibutuhkan sekali. *b = 0;
hanya dibutuhkan sekali.
#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
membantu memangkas semua ruang putih.
strndup
untuk membuat buffer string baru dengan mengecualikan spasi.strndup()
bukan bagian dari standar C tetapi hanya Posix. Tetapi karena cukup mudah untuk diimplementasikan, itu bukan masalah besar.
trim_space("")
kembali NULL
. Saya akan mengharapkan pointer ke ""
. int len;
seharusnya size_t len;
. isspace(in[len - 1])
UB kapan in[len - 1] < 0
.
while (isspace((unsigned char) *in) in++;
sebelumnya len = strlen(in);
akan lebih efisien daripada yang belakanganwhile(len && *in && isspace(*in)) ++in, --len;
Secara pribadi, saya akan roll sendiri. Anda dapat menggunakan strtok, tetapi Anda harus berhati-hati melakukannya (terutama jika Anda menghapus karakter utama) sehingga Anda tahu memori apa itu.
Menyingkirkan spasi tambahan itu mudah, dan cukup aman, karena Anda bisa menempatkan 0 di atas spasi terakhir, menghitung mundur dari akhir. Menyingkirkan ruang terdepan berarti memindahkan berbagai hal. Jika Anda ingin melakukannya di tempat (mungkin masuk akal) Anda bisa terus menggeser semuanya kembali satu karakter sampai tidak ada ruang terkemuka. Atau, agar lebih efisien, Anda bisa menemukan indeks karakter non-spasi pertama, dan menggeser semuanya ke belakang dengan angka itu. Atau, Anda bisa menggunakan pointer ke karakter non-spasi pertama (tapi kemudian Anda harus berhati-hati dengan cara yang sama seperti yang Anda lakukan dengan strtok).
#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;
}
Agak terlambat ke pertandingan, tapi aku akan membuang rutinitasku ke medan. Mereka mungkin bukan yang paling efisien, tapi saya percaya itu benar dan sederhana (dengan rtrim()
mendorong amplop kompleksitas):
#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
argumen isspace()
ke (unsigned char)
untuk menghindari perilaku tidak terdefinisi pada nilai negatif yang berpotensi. Juga hindari memindahkan string jika dalam ltrim()
jika tidak perlu.
Sebagian besar jawaban sejauh ini melakukan salah satu dari yang berikut:
strlen()
dulu, buat pass kedua melalui seluruh string.Versi ini hanya membuat satu operan dan tidak mundur. Oleh karena itu mungkin berkinerja lebih baik daripada yang lain, meskipun hanya jika itu umum untuk memiliki ratusan ruang tambahan (yang tidak biasa ketika berhadapan dengan output dari query 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';
}
strspn()
dan strcspn()
dalam loop ketat. Ini sangat tidak efisien dan overhead akan mengerdilkan keuntungan yang tidak terbukti dari satu umpan maju. strlen()
biasanya diperluas sesuai dengan kode yang sangat efisien, bukan masalah nyata. Memotong awal dan akhir string akan jauh lebih cepat daripada menguji setiap karakter dalam string untuk putih bahkan dalam kasus khusus string dengan sangat sedikit atau tidak ada karakter non-putih.
Ini adalah implementasi sesingkat mungkin yang dapat saya pikirkan:
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; }
Fungsi-fungsi ini akan memodifikasi buffer asli, jadi jika dialokasikan secara dinamis, pointer asli dapat dibebaskan.
#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()
memanggil perilaku yang tidak terdefinisi pada string kosong. lstrip()
tidak perlu lambat pada string dengan bagian awal panjang karakter spasi. isspace()
tidak boleh dilewatkan char
argumen karena memunculkan perilaku tidak terdefinisi pada nilai negatif yang berbeda dari EOF
.
Apa pendapat Anda tentang menggunakan fungsi StrTrim yang didefinisikan dalam header Shlwapi.h.? Ini lurus ke depan, bukan mendefinisikan Anda sendiri.
Detailnya dapat ditemukan di:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773454(v=vs.85).aspx
Jika Anda memiliki
char ausCaptain[]="GeorgeBailey ";
StrTrim(ausCaptain," ");
ini akan memberi ausCaptain
sebagai "GeorgeBailey"
tidak "GeorgeBailey "
.
Untuk memangkas dawai saya dari kedua sisi saya menggunakan oldie tetapi gooody;) Ini dapat memangkas apa pun dengan ascii kurang dari satu ruang, yang berarti bahwa chars kontrol akan dipangkas juga!
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
bukan unsigned int
. Kode memiliki banyak tes berlebihan dan mengaktifkan perilaku tidak terdefinisi strncpy(strData,&strData[S],L)
karena array sumber dan tujuan tumpang tindih. Gunakan memmove()
sebagai ganti strncpy()
.
Saya hanya memasukkan kode karena kode yang diposting sejauh ini tampaknya kurang optimal (dan saya belum memiliki perwakilan untuk berkomentar.)
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()
adalah ekstensi GNU. Jika Anda tidak memilikinya atau setara, gulirkan sendiri. Sebagai contoh:
r = strdup(s + start);
r[end-start] = '\0';
isspace(0)
didefinisikan sebagai salah, Anda dapat menyederhanakan kedua fungsi. Juga pindahkan bagian memmove()
dalam if
blok.
Di sini saya menggunakan alokasi memori dinamis untuk memotong string input ke fungsi trimStr. Pertama, kami menemukan berapa banyak karakter tidak kosong yang ada di string input. Kemudian, kami mengalokasikan array karakter dengan ukuran itu dan merawat karakter terminasi nol. Ketika kita menggunakan fungsi ini, kita perlu membebaskan memori di dalam fungsi utama.
#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;
}
Inilah cara saya melakukannya. Ini memotong string di tempat, jadi tidak perlu khawatir tentang membatalkan alokasi string yang dikembalikan atau kehilangan pointer ke string yang dialokasikan. Ini mungkin bukan jawaban sesingkat mungkin, tetapi harus jelas bagi sebagian besar pembaca.
#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;
}