Saya mencoba untuk mendapatkan beberapa data dari pengguna dan mengirimkannya ke fungsi lain di gcc. Kodenya kira-kira seperti ini.
printf("Enter your Name: ");
if (!(fgets(Name, sizeof Name, stdin) != NULL)) {
fprintf(stderr, "Error reading Name.\n");
exit(1);
}
Namun, pada akhirnya saya menemukan \n
karakter baris baru . Jadi jika saya memasukkannya John
akhirnya mengirim John\n
. Bagaimana cara menghapus itu \n
dan mengirim string yang tepat.
if (!fgets(Name, sizeof Name, stdin))
(paling tidak jangan gunakan dua negasi,! dan! =)if (fgets(Name, sizeof Name, stdin)) {
.if (fgets(Name, sizeof Name, stdin) == NULL ) {
!
:Jawaban:
Cara yang agak jelek:
Cara yang agak aneh:
Perhatikan bahwa
strtok
fungsi tidak berfungsi seperti yang diharapkan jika pengguna memasukkan string kosong (yaitu hanya menekan Enter). Itu membuat\n
karakter utuh.Ada yang lain juga, tentu saja.
sumber
strtok()
akan aman utas (ini akan menggunakan penyimpanan lokal utas untuk keadaan 'antar panggilan'). Yang mengatakan, itu masih lebih baik untuk menggunakanstrtok_r()
varian non-standar (tapi cukup umum) .strtok
pendekatan Anda (dan bekerja dengan input kosong). Bahkan, cara yang baik untuk mengimplementasikannyastrtok
adalah menggunakanstrcspn
danstrspn
.*strchrnul(Name, '\n') = '\0';
.strchr(Name, '\n') == NULL
, selain dari "input terlalu lama untuk buffer, flag error", kemungkinan lain ada: Teks terakhirstdin
tidak berakhir dengan'\n'
atau karakter null tertanam yang jarang dibaca.Mungkin solusi yang paling sederhana menggunakan salah satu fungsi sedikit diketahui favorit saya,
strcspn()
:Jika Anda ingin juga menangani
'\r'
(katakanlah, jika aliran adalah biner):Fungsi menghitung jumlah karakter hingga mencapai a
'\r'
atau a'\n'
(dengan kata lain, ia menemukan yang pertama'\r'
atau'\n'
). Jika tidak mengenai apa pun, itu berhenti di'\0'
(mengembalikan panjang string).Perhatikan bahwa ini berfungsi dengan baik bahkan jika tidak ada baris baru, karena
strcspn
berhenti di a'\0'
. Dalam hal ini, seluruh baris hanya diganti'\0'
dengan'\0'
.sumber
buffer
daripada yang dimulai dengan'\0'
, sesuatu yang menyebabkan kesedihan untukbuffer[strlen(buffer) - 1] = '\0';
pendekatan tersebut.strcspn()
. Salah satu fungsi yang lebih berguna di perpustakaan, IMO. Saya telah memutuskan untuk menulis dan menerbitkan banyak peretasan C yang umum seperti ini hari ini; sebuahstrtok_r
implementasi menggunakanstrcspn
danstrspn
adalah salah satu yang pertama: codepad.org/2lBkZk0w ( Peringatan: Saya tidak bisa menjamin bahwa itu tanpa bug, itu ditulis dengan tergesa-gesa dan mungkin memiliki beberapa). Saya tidak tahu di mana saya akan mempublikasikannya, tetapi saya bermaksud membuatnya dalam semangat "hacks twiddling" yang terkenal.fgets()
strcspn()
strlen
fgets()
input . Yang selalu juga merupakan baris pertama.sumber
fgets(buf, size, ....)
->strlen(buf) == 0
. 1)fgets()
dibaca sebagai yang pertamachar
a'\0'
. 2)size == 1
3)fgets()
kembaliNULL
makabuf
isinya bisa apa saja. (Kode OP tidak menguji NULL) Sarankan:size_t ln = strlen(name); if (ln > 0 && name[ln-1] == '\n') name[--ln] = '\0';
ln
akan menjadi -1, simpan untuk faktasize_t
tidak ditandatangani, sehingga menulis ke memori acak. Saya pikir Anda ingin menggunakanssize_t
dan periksaln
adalah> 0.strlen
) dapat diimplementasikan jauh lebih efisien daripada pencarian char-by-char biasa. Untuk alasan itulah saya menganggap solusi ini lebih baik daripada yang berbasisstrchr
ataustrcspn
.Di bawah ini adalah pendekatan cepat untuk menghapus potensi
'\n'
dari string yang disimpan olehfgets()
.Ini digunakan
strlen()
, dengan 2 tes.Sekarang gunakan
buffer
danlen
sesuai kebutuhan.Metode ini memiliki manfaat sampingan dari
len
nilai untuk kode selanjutnya. Ini dapat dengan mudah lebih cepat daripadastrchr(Name, '\n')
. Ref YMMV, tetapi kedua metode bekerja.buffer
, dari aslinyafgets()
tidak akan berisi dalam"\n"
beberapa keadaan:A) Garis terlalu panjang untuk
buffer
hanyachar
mendahului'\n'
disimpan dalambuffer
. Karakter yang belum dibaca tetap dalam aliran.B) Baris terakhir dalam file tidak diakhiri dengan a
'\n'
.Jika input telah menanamkan karakter nol
'\0'
di suatu tempat, panjangnya dilaporkan olehstrlen()
menyertakan tidak akan menyertakan'\n'
lokasi.Beberapa masalah jawaban lain:
strtok(buffer, "\n");
gagal untuk menghapus'\n'
saatbuffer
ini"\n"
. Dari ini jawaban - diubah setelah jawaban ini untuk memperingatkan batasan ini.Berikut ini gagal pada kesempatan langka ketika yang pertama
char
dibacafgets()
adalah'\0'
. Ini terjadi ketika input dimulai dengan tertanam'\0'
. Lalubuffer[len -1]
menjadibuffer[SIZE_MAX]
mengakses memori tentunya di luar rentang yang sahbuffer
. Sesuatu yang mungkin dicoba atau ditemukan oleh peretas dengan bodoh membaca file teks UTF16. Ini adalah keadaan jawaban ketika jawaban ini ditulis. Kemudian non-OP diedit untuk memasukkan kode seperti cek jawaban ini""
.sprintf(buffer,"%s",buffer);
adalah perilaku yang tidak terdefinisi: Ref . Lebih lanjut, ini tidak menyimpan spasi putih depan, pemisah, atau tertinggal. Sekarang dihapus .[Sunting karena jawaban nanti yang bagus ] Tidak ada masalah dengan 1 liner
buffer[strcspn(buffer, "\n")] = 0;
selain kinerja dibandingkan denganstrlen()
pendekatan. Kinerja dalam pemangkasan biasanya bukan masalah yang diberikan kode melakukan I / O - lubang hitam waktu CPU. Jika kode berikut membutuhkan panjang string atau sangat sadar kinerja, gunakanstrlen()
pendekatan ini . Lainstrcspn()
adalah alternatif yang baik.sumber
strlen(buffer)
ketika ukuran buffer dialokasikan secara dinamis menggunakanmalloc
?buffer = malloc(allocation_size); length = strlen(buffer);
buruk - data di memori yang ditunjuk olehbuffer
tidak diketahui.buffer = malloc(allocation_size_4_or_more); strcpy(buffer, "abc"); length = strlen(buffer);
tidak apaLangsung untuk menghapus '\ n' dari output fgets jika setiap baris memiliki '\ n'
Jika tidak:
sumber
strnlen
daripadastrlen
.n
tidak secara ajaib meningkatkan keamanan, dalam hal ini justru akan membuat kode lebih berbahaya. Demikian pula denganstrncpy
, fungsi yang sangat tidak aman. Posting yang Anda tautkan adalah saran yang buruk.""
). Juga tidakstrlen()
kembali .size_t
int
Untuk pemangkasan tunggal '\ n',
untuk beberapa pemangkasan '\ n',
sumber
if
saat Anda cukup menulis satu syarat menggunakan&&
? Bahwawhile
lingkaran memiliki struktur yang aneh; mungkin sajawhile (length > 0 && string[length-1] == '\n') { --length; string[length] = '\0'; }
.size_t length = strlen(string); if (length > 0 && string[length-1] == '\n') { string[length-1] = '\0'; }
. Ini juga mencerminkan definisi kedua yang lebih baik (hanya menggunakanif
alih-alihwhile
).Cara Newbie saya ;-) Tolong beri tahu saya kalau itu benar. Tampaknya berfungsi untuk semua kasus saya:
sumber
Langkah-langkah untuk menghapus karakter baris baru dengan cara yang mungkin paling jelas:
NAME
dengan menggunakanstrlen()
, headerstring.h
. Perhatikan bahwastrlen()
tidak termasuk penghentian\0
.\0
karakter (string kosong). Dalam hal inisl
adalah0
karenastrlen()
seperti yang saya katakan di atas tidak menghitung\0
dan berhenti pada kemunculan pertama itu:'\n'
. Jika demikian, ganti\n
dengan a\0
. Perhatikan bahwa penghitungan indeks dimulai pada0
jadi kita perlu melakukanNAME[sl - 1]
:Catatan jika Anda hanya menekan Enter pada
fgets()
permintaan string (konten string hanya terdiri dari karakter baris baru) string diNAME
akan menjadi string kosong sesudahnya.if
pernyataan dengan menggunakan operator logika&&
:Jika Anda lebih suka fungsi untuk menggunakan teknik ini dengan menangani
fgets
string keluaran secara umum tanpa mengetik ulang setiap waktu, berikut adalahfgets_newline_kill
:Dalam contoh yang Anda berikan, itu akan menjadi:
Perhatikan bahwa metode ini tidak berfungsi jika string input telah tertanam
\0
di dalamnya. Jika itu terjadi makastrlen()
hanya akan mengembalikan jumlah karakter sampai yang pertama\0
. Tapi ini bukan pendekatan yang umum, karena fungsi membaca string biasanya berhenti pada awalnya\0
dan mengambil string sampai karakter nol itu.Selain dari pertanyaan itu sendiri. Cobalah untuk menghindari negations ganda yang membuat unclearer kode Anda:
if (!(fgets(Name, sizeof Name, stdin) != NULL) {}
. Anda cukup melakukannyaif (fgets(Name, sizeof Name, stdin) == NULL) {}
.sumber
\n
dengan\0
di akhir string adalah cara "menghapus" baris baru. Tetapi mengganti\n
karakter dalam string secara fundamental mengubah string. Tidak jarang memiliki string dengan beberapa karakter baris baru yang disengaja, dan ini akan secara efektif memotong ujung string tersebut. Untuk menghapus baris baru tersebut, isi array harus bergeser ke kiri untuk menulis berlebihan\n
.fgets()
?fgets()
. Tapi saya tidak mengerti keberatan Anda: Anda adalah orang yang mengusulkan kode untuk menangani beberapa baris baru.strlen
dll. Pembenaran karena tidak menjadi duplikat: 1. Penjelasan kode dengan langkah-langkah. 2. Diberikan sebagai solusi berbasis fungsi dan konteks. 3. Petunjuk untuk menghindari ekspresi negasi ganda.Tim onesebagai liner luar biasa untuk string yang diperoleh melalui panggilan ke gadget, karena Anda tahu mereka mengandung satu baris baru di akhir.
Jika Anda berada dalam konteks yang berbeda dan ingin menangani string yang mungkin berisi lebih dari satu baris baru, Anda mungkin mencari strrspn. Ini bukan POSIX, artinya Anda tidak akan menemukannya di semua Unix. Saya menulis satu untuk kebutuhan saya sendiri.
Bagi mereka yang mencari setara Perl chomp di C, saya pikir ini dia (chomp hanya menghapus baris baru).
Fungsi strrcspn:
sumber
'\n'
(atau jika stringnya""
).strrcspn
ketika tidak ada\n
.goto end;
bukanreturn len;
?goto
s dalam kode Anda: yang tidak bergunagoto
yang dapat diganti denganreturn
pernyataan dan mundurgoto
yang dianggap jahat. Menggunakan alatstrchr
bantu implementasikanstrrspn
danstrrcspn
dengan cara yang lebih sederhana:size_t strrspn(const char *s, const char *accept) { size_t len = strlen(s); while (len > 0 && strchr(accept, s[len - 1])) { len--; } return len; }
dansize_t strrcspn(const char *s, const char *reject) { size_t len = strlen(s); while (len > 0 && !strchr(reject, s[len - 1])) { len--; } return len; }
Jika menggunakan
getline
adalah opsi - Tidak mengabaikan masalah keamanannya dan jika Anda ingin menguatkan pointer - Anda dapat menghindari fungsi string karenagetline
mengembalikan jumlah karakter. Sesuatu seperti di bawah iniCatatan : [ masalah keamanan ] dengan
getline
seharusnya tidak diabaikan.sumber
Fungsi di bawah ini adalah bagian dari pustaka pemrosesan string yang saya pertahankan di Github. Ini menghapus dan karakter yang tidak diinginkan dari string, persis apa yang Anda inginkan
Contoh penggunaan bisa
Anda mungkin ingin memeriksa fungsi lain yang tersedia, atau bahkan berkontribusi pada proyek :) https://github.com/fnoyanisi/zString
sumber
*
di*src++;
dan membuatbad
,token
dand
const char *
. Juga mengapa tidak menggunakanstrchr
bukanzChrSearch
?*src
tidak bisa'\0'
dalamzStrrmv
fungsi Anda .strchr
Anda harus mencobanya. Kode ini pada dasarnya loop melalui string sampai menemukan '\ n'. Ketika ditemukan '\ n' akan digantikan oleh terminator karakter nol '\ 0'
Perhatikan bahwa Anda membandingkan karakter dan bukan string di baris ini, maka tidak perlu menggunakan strcmp ():
karena Anda akan menggunakan tanda kutip tunggal dan bukan tanda kutip ganda. Berikut ini tautan tentang tanda kutip tunggal vs ganda jika Anda ingin tahu lebih banyak
sumber
for(int i = 0; i < strlen(Name); i++ )
akan memanggilstrlen(Name)
berkali-kali (perubahan loopName[]
) sehingga dengan panjangN
, ini adalahO(N*N)
solusi. Hanya 1 panggilan kestrlen(Name)
, jika ada, yang diperlukan untuk memberikan solusi O (N) `. Tidak jelas mengapaint i
digunakansize_t i
. Pertimbangkanfor(size_t i = 0; i < Name[i]; i++ )
for (size_t i = 0; Name[i]; i++) { if (Name[i] == '\n') { Name[i] = '\0'; break; } }
Coba yang ini:
sumber
len = strlen(str)
mungkin meluap:strlen
kembalisize_t
, tidakint
. Ada apa dengan persyaratan anehif (len>0) if (...)
? Apakah kamu tidak tahu tentang&&
? Jika Anda akan menghapus beberapa instance trailing CR / LF, mengapa membatasi diri hingga 5? Mengapa tidak menghapus semuanya? Mengapa fungsi memilikiint
tipe pengembalian ketika selalu kembali0
? Kenapa tidak kembali sajavoid
?