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
untukunsigned 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:
Uji kebenaran:
File sumber tadinya trim.c. Dikompilasi dengan 'cc -Wall trim.c -o trim'.
sumber
isspace
untukunsigned 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.
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 ().
sumber
trim()
memanggil UB jikas
adalah""
yang pertamaisspace()
panggilan akanisspace(p[-1])
danp[-1]
tidak selalu referensi lokasi hukum.isspace
untukunsigned char
, jika Anda menjalankan perilaku undefined.if(l==0)return;
untuk menghindari str nol panjangInilah 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).
isi dari strlib.h:
isi dari strlib.c:
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.sumber
dtab[*d]
tidak dilempar*d
keunsigned int
sebelum menggunakannya sebagai indeks array. Pada sistem dengan char yang ditandatangani ini akan membacadtab[-127]
yang akan menyebabkan bug dan mungkin crash.dtab[*delim++]
karenachar
nilai indeks harus dilemparkan keunsigned char
. Kode mengasumsikan 8-bitchar
.delim
harus dinyatakan sebagaiconst char *
.dtab[0xFF & (unsigned int)*d]
akan lebih jelas sebagaidtab[(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.
sumber
while ((end >= begin) && isspace(str[end]))
mencegah UB ketikastr is
"". Prevents
str [-1] `.isspace
untukunsigned char
, jika Anda menjalankan perilaku undefined.<ctype.h>
dimaksudkan untuk bekerja dengan int, yang mewakiliunsigned char
nilai khusus atauEOF
. 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
ditandatanganichar
, masukkan*s
keunsigned char
.@chqrlie berkomentar di atas tidak menggeser string yang dipangkas. Untuk melakukannya ....
sumber
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 .
sumber
Satu lagi, dengan satu baris melakukan pekerjaan nyata:
sumber
%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:
sumber
isspace
untukunsigned 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.
sumber
'\0'
dan kemudian diuji denganisspace()
. Tampaknya boros untuk menguji semua karakterisspace()
. Mundur dari ujung tali harus lebih efisien untuk kasus non patologis.trim()
BAIK. Kasus sudut:trim2(char *d, const char *s)
memiliki masalah saatd,s
tumpang tindih dans < d
.trim()
bersikap? Anda meminta untuk memotong dan menyalin string ke memori yang ditempati oleh string itu sendiri. Tidak sepertimemmove()
ini, ini membutuhkan penentuan panjang string sumber sebelum melakukan trim itu sendiri, yang membutuhkan pemindaian seluruh string tambahan waktu. Lebih baik menulisrtrim2()
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:
Kita dapat menemukan posisi karakter non-spasi putih terakhir dengan dua gerakan sepele yang serupa:
(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 sepertichomp
fungsi PHperytonby . Apa fungsi seperti itu di C kembali?sumber
do { q--; } ...
tahu*q != 0
.Gunakan pustaka string , misalnya:
... 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.
sumber
Jika Anda menggunakan
glib
, maka Anda dapat menggunakan g_strstripsumber
Untuk menjaga pertumbuhan ini, satu opsi lagi dengan string yang dapat dimodifikasi:
sumber
strlen()
mengembalikan asize_t
yang dapat melebihi kisaranint
. ruang putih tidak terbatas pada karakter ruang. Akhirnya tetapi yang paling penting: Perilaku tidak terdefinisi padastrcpy(string, string + i * sizeof(char));
karena array sumber dan tujuan tumpang tindih. Gunakanmemmove()
sebagai gantistrcpy()
.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 Andaend = ++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.
sumber
isspace(*str)
UB kapan*str < 0
.size_t n
bagus, namun antarmuka tidak memberi tahu pemanggil dengan cara apa pun ketikan
terlalu kecil untuk string yang dipangkas sepenuhnya. Pertimbangkantrim(out, 12, "delete data not")
Cara termudah untuk melewati spasi dalam string adalah, imho,
sumber
" 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.sumber
b > str
tes hanya dibutuhkan sekali.*b = 0;
hanya dibutuhkan sekali.isspace
membantu memangkas semua ruang putih.strndup
untuk membuat buffer string baru dengan mengecualikan spasi.sumber
strndup()
bukan bagian dari standar C tetapi hanya Posix. Tetapi karena cukup mudah untuk diimplementasikan, itu bukan masalah besar.trim_space("")
kembaliNULL
. Saya akan mengharapkan pointer ke""
.int len;
seharusnyasize_t len;
.isspace(in[len - 1])
UB kapanin[len - 1] < 0
.while (isspace((unsigned char) *in) in++;
sebelumnyalen = 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).
sumber
sumber
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):sumber
char
argumenisspace()
ke(unsigned char)
untuk menghindari perilaku tidak terdefinisi pada nilai negatif yang berpotensi. Juga hindari memindahkan string jika dalamltrim()
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.)
sumber
strspn()
danstrcspn()
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:
sumber
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.
sumber
rstrip()
memanggil perilaku yang tidak terdefinisi pada string kosong.lstrip()
tidak perlu lambat pada string dengan bagian awal panjang karakter spasi.isspace()
tidak boleh dilewatkanchar
argumen karena memunculkan perilaku tidak terdefinisi pada nilai negatif yang berbeda dariEOF
.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 "
.sumber
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!
sumber
size_t
bukanunsigned int
. Kode memiliki banyak tes berlebihan dan mengaktifkan perilaku tidak terdefinisistrncpy(strData,&strData[S],L)
karena array sumber dan tujuan tumpang tindih. Gunakanmemmove()
sebagai gantistrncpy()
.Saya hanya memasukkan kode karena kode yang diposting sejauh ini tampaknya kurang optimal (dan saya belum memiliki perwakilan untuk berkomentar.)
strndup()
adalah ekstensi GNU. Jika Anda tidak memilikinya atau setara, gulirkan sendiri. Sebagai contoh:sumber
isspace(0)
didefinisikan sebagai salah, Anda dapat menyederhanakan kedua fungsi. Juga pindahkan bagianmemmove()
dalamif
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.
sumber
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.
sumber
sumber