Katakanlah saya memiliki kode berikut:
String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar."
story = story.replace("foo", word1);
story = story.replace("bar", word2);
Setelah kode ini berjalan, nilainya story
akan"Once upon a time, there was a foo and a foo."
Masalah serupa terjadi jika saya menggantinya dengan urutan yang berlawanan:
String word1 = "bar";
String word2 = "foo";
String story = "Once upon a time, there was a foo and a bar."
story = story.replace("bar", word2);
story = story.replace("foo", word1);
Nilai story
akan"Once upon a time, there was a bar and a bar."
Tujuan saya adalah story
menjadi "Once upon a time, there was a bar and a foo."
Bagaimana saya bisa mencapai itu?
swap(String s1, String s2, String s3)
yang menukar semua kejadians2
dengans3
, dan sebaliknya.Jawaban:
Gunakan
replaceEach()
metode dari Apache Commons StringUtils :sumber
null
disahkan.Anda menggunakan nilai perantara (yang belum ada dalam kalimat).
Sebagai tanggapan terhadap kritik: jika Anda menggunakan string tidak umum yang cukup besar seperti zq515sqdqs5d5sq1dqs4d1q5dqqé "& é5d4sqjshsjddhodfqsqc, nvùq ^ μù; d & dsq: d:; bahkan di mana tidak ada gunanya, tidak akan terjadi di mana pun. bahwa pengguna akan pernah memasukkan ini. Satu-satunya cara untuk mengetahui apakah pengguna akan adalah dengan mengetahui kode sumber dan pada saat itu Anda dengan tingkat kekhawatiran lainnya.
Ya, mungkin ada cara regex mewah. Saya lebih suka sesuatu yang dapat dibaca yang saya tahu tidak akan pecah pada saya juga.
Juga mengulangi saran luar biasa yang diberikan oleh @David Conrad di komentar :
sumber
Anda dapat mencoba sesuatu seperti ini, menggunakan
Matcher#appendReplacement
danMatcher#appendTail
:sumber
foo
,bar
danstory
semua memiliki nilai yang tidak diketahui?"foo"
dan"bar"
string pengganti seperti OP dalam kodenya, tetapi jenis pendekatan yang sama akan berfungsi dengan baik bahkan jika nilai-nilai itu tidak diketahui (Anda harus menggunakanif
/else if
bukannyaswitch
dalamwhile
-lompat).Pattern.quote
akan berguna, atau\Q
dan\E
.(foo)|(bar)
dan kemudian memeriksam.group(1) != null
, untuk menghindari mengulangi kata-kata yang cocok.Ini bukan masalah yang mudah. Dan semakin banyak parameter penggantian pencarian yang Anda miliki, semakin sulit hasilnya. Anda memiliki beberapa opsi, tersebar di palet jelek, elegan, dan boros:
Gunakan
StringUtils.replaceEach
dari Apache Commons seperti yang disarankan @AlanHay . Ini adalah opsi yang baik jika Anda bebas menambahkan dependensi baru di proyek Anda. Anda mungkin beruntung: ketergantungan mungkin sudah termasuk dalam proyek AndaGunakan pengganti sementara seperti yang disarankan @Jeroen , dan lakukan penggantian dalam 2 langkah:
Ini bukan pendekatan yang bagus, karena beberapa alasan: perlu memastikan bahwa tag yang digunakan pada langkah pertama benar-benar unik; ia melakukan lebih banyak operasi penggantian string daripada yang benar-benar diperlukan
Buat regex dari semua pola dan gunakan metode dengan
Matcher
danStringBuffer
seperti yang disarankan oleh @arshajii . Ini tidak buruk, tetapi tidak terlalu bagus, karena membangun regex adalah jenis peretasan, dan itu melibatkanStringBuffer
yang keluar dari mode beberapa waktu lalu yang mendukungStringBuilder
.Gunakan solusi rekursif yang diusulkan oleh @mjolka , dengan memecah string pada pola yang cocok, dan berulang pada segmen yang tersisa. Ini adalah solusi yang bagus, ringkas dan cukup elegan. Kelemahannya adalah berpotensi banyak operasi substring dan gabungan, dan batas ukuran tumpukan yang berlaku untuk semua solusi rekursif
Membagi teks menjadi kata-kata dan menggunakan stream Java 8 untuk melakukan penggantian secara elegan seperti yang disarankan @msandiford , tetapi tentu saja itu hanya berfungsi jika Anda setuju dengan pemisahan pada batas kata, yang membuatnya tidak cocok sebagai solusi umum
Ini versi saya, berdasarkan ide yang dipinjam dari implementasi Apache . Ini tidak sederhana atau elegan, tetapi berfungsi, dan harus relatif efisien, tanpa langkah yang tidak perlu. Singkatnya, ini berfungsi seperti ini: berulang kali menemukan pola pencarian yang cocok berikutnya dalam teks, dan gunakan a
StringBuilder
untuk mengakumulasi segmen yang tak tertandingi dan penggantian.Tes unit:
sumber
Cari kata pertama yang akan diganti. Jika ada di string, kembalilah pada bagian string sebelum terjadinya, dan pada bagian string setelah terjadinya.
Jika tidak, lanjutkan dengan kata berikutnya yang akan diganti.
Implementasi yang naif mungkin terlihat seperti ini
Penggunaan sampel:
Keluaran:
Versi yang kurang naif:
Sayangnya, Java
String
tidak memilikiindexOf(String str, int fromIndex, int toIndex)
metode. Saya telah menghilangkan implementasi diindexOf
sini karena saya tidak yakin itu benar, tetapi dapat ditemukan di ideone , bersama dengan beberapa timing kasar dari berbagai solusi yang diposting di sini.sumber
One-liner di Java 8:
?<=
,?=
): http://www.regular-expressions.info/lookaround.htmlsumber
Berikut adalah kemungkinan streaming Java 8 yang mungkin menarik bagi sebagian orang:
Berikut ini adalah perkiraan algoritma yang sama di Java 7:
sumber
Jika Anda ingin mengganti kata dalam kalimat yang dipisahkan oleh spasi seperti yang ditunjukkan pada contoh Anda, Anda dapat menggunakan algoritma sederhana ini.
Jika pemisahan pada ruang tidak dapat diterima, seseorang dapat mengikuti algoritma alternatif ini. Anda harus menggunakan string yang lebih panjang terlebih dahulu. Jika string foo dan bodoh, Anda harus menggunakan bodoh dulu dan kemudian foo.
sumber
Inilah jawaban yang tidak terlalu rumit menggunakan Peta.
Dan metode disebut
Outputnya adalah: luar biasa adalah Raffy, Raffy Raffy mengagumkan mengagumkan
sumber
replaced.replaceAll("Raffy", "Barney");
setelah ini akan membuatnya menjadi legen ... tunggu; Dary !!!Jika Anda ingin dapat menangani beberapa kemunculan string pencarian yang akan diganti, Anda dapat melakukannya dengan mudah dengan memisahkan string pada setiap istilah pencarian, lalu menggantinya. Berikut ini sebuah contoh:
sumber
Anda dapat mencapai tujuan Anda dengan blok kode berikut:
Itu menggantikan kata-kata terlepas dari urutannya. Anda dapat memperluas prinsip ini menjadi metode utilitas, seperti:
Yang akan dikonsumsi sebagai:
sumber
Ini berfungsi dan sederhana:
Anda menggunakannya seperti ini:
Catatan: ini bergantung pada Strings yang tidak mengandung karakter
\ufdd0
, yang merupakan karakter yang secara permanen disediakan untuk penggunaan internal oleh Unicode (Lihat http://www.unicode.org/faq/private_use.html ):Saya pikir itu tidak perlu, tetapi jika Anda ingin benar-benar aman, Anda dapat menggunakan:
sumber
Bertukar Hanya Satu Kejadian
Jika hanya ada satu kejadian dari masing-masing string yang dapat ditukar di input, Anda dapat melakukan hal berikut:
Sebelum melanjutkan ke penggantian apa pun, dapatkan indeks kemunculan kata-kata tersebut. Setelah itu kami hanya mengganti kata yang ditemukan di indeks ini, dan tidak semua kejadian. Solusi ini menggunakan
StringBuilder
dan tidak menghasilkanString
seperti perantaraString.replace()
.Satu hal yang perlu diperhatikan: jika kata swapable memiliki panjang yang berbeda, setelah ganti pertama indeks kedua mungkin berubah (jika kata pertama muncul sebelum kata kedua) tepat dengan perbedaan 2 panjang. Jadi, menyelaraskan indeks kedua akan memastikan ini berfungsi bahkan jika kita bertukar kata dengan panjang yang berbeda.
Bertukar Jumlah Kejadian Sewenang-wenang
Analog dengan kasus sebelumnya, pertama-tama kita akan mengumpulkan indeks (kemunculan) kata-kata, tetapi dalam kasus ini akan menjadi daftar bilangan bulat untuk setiap kata, bukan hanya satu
int
. Untuk ini, kami akan menggunakan metode utilitas berikut:Dan menggunakan ini kita akan mengganti kata-kata dengan yang lain dengan mengurangi indeks (yang mungkin perlu berganti-ganti antara 2 kata swapable) sehingga kita bahkan tidak perlu memperbaiki indeks setelah diganti:
sumber
indexOf
cocok mungkin tidak memiliki panjang yang sama dengan searchstring berkat keistimewaan kesetaraan string unicode.String
adalah array karakter dan bukan array byte. Semua metodeString
danStringBuilder
beroperasi pada karakter bukan pada byte, yang "bebas encoding". Dengan demikianindexOf
kecocokan memiliki panjang (karakter) yang persis sama dengan string pencarian.ä
dapat dikodekan sebagai satu codepoint atau sebagai yanga
diikuti oleh penggabungan¨
. Ada juga beberapa codepoint yang diabaikan, seperti zero-width (non) joiners. Tidak masalah jika string terdiri dari byte, karakter atau apa pun, tetapi aturan perbandingan mana yangindexOf
digunakan. Ini mungkin hanya menggunakan unit kode dengan perbandingan unit kode ("Ordinal") atau mungkin menerapkan kesetaraan unicode. Saya tidak tahu mana yang dipilih java."ab\u00ADc".IndexOf("bc")
kembali1
dalam .net yang cocok dengan string dua karakterbc
ke string tiga karakter."ab\u00ADc".indexOf("bc")
pengembalian-1
yang berarti"bc"
tidak ditemukan di"ab\u00ADc"
. Jadi tetap ada bahwa di Jawa algoritma di atas berfungsi,indexOf()
kecocokan memiliki panjang (karakter) yang sama persis dengan string pencarian, danindexOf()
hanya melaporkan kecocokan jika charsequences (codepoints) cocok.Sangat mudah untuk menulis metode untuk melakukan ini menggunakan
String.regionMatches
:Pengujian:
Keluaran:
Tidak segera jelas, tetapi fungsi seperti ini masih bisa bergantung pada urutan penggantian yang ditentukan. Mempertimbangkan:
Keluaran:
Tetapi balikkan penggantian:
Keluaran:
Ups! :)
Oleh karena itu kadang-kadang berguna untuk memastikan mencari kecocokan terpanjang (seperti
strtr
fungsi PHP , misalnya). Versi metode ini akan melakukan itu:Perhatikan bahwa metode di atas peka terhadap huruf besar-kecil. Jika Anda memerlukan versi case-insensitive, mudah untuk memodifikasi yang di atas karena
String.regionMatches
dapat mengambilignoreCase
parameter.sumber
Jika Anda tidak menginginkan dependensi, Anda cukup menggunakan array yang memungkinkan perubahan satu kali saja. Ini bukan solusi yang paling efisien, tetapi harus bekerja.
Lalu, itu akan berhasil.
sumber
Anda sedang melakukan beberapa operasi pencarian-ganti pada input. Ini akan menghasilkan hasil yang tidak diinginkan ketika string pengganti berisi string pencarian. Pertimbangkan bar foo->, contoh bar-foo, berikut adalah hasil untuk setiap iterasi:
Anda perlu melakukan penggantian dalam satu iterasi tanpa kembali. Solusi brute-force adalah sebagai berikut:
Fungsi seperti
String.indexOfAny(String[]) -> int[]{index, whichString}
itu akan berguna. Berikut ini sebuah contoh (bukan yang paling efisien):Beberapa tes:
Demo di IDEONE
Demo di IDEONE, kode alternatif
sumber
Anda selalu bisa menggantinya dengan kata yang Anda yakin akan muncul di tempat lain di string, dan kemudian lakukan penggantian kedua nanti:
Perhatikan bahwa ini tidak akan berfungsi jika
"StringYouAreSureWillNeverOccur"
terjadi.sumber
Pertimbangkan untuk menggunakan StringBuilder
Kemudian simpan indeks di mana setiap string harus dimulai. Jika Anda menggunakan karakter tempat penampung di setiap posisi, lalu hapus, dan masukkan string pengguna. Anda kemudian dapat memetakan posisi ujung dengan menambahkan panjang string ke posisi awal.
sumber
Yang hanya bisa saya bagikan adalah metode saya sendiri.
Anda dapat menggunakan sementara
String temp = "<?>";
atauString.Format();
Ini adalah contoh kode saya yang dibuat di aplikasi konsol via c # - "Hanya Ide, Tidak Tepat Jawaban" .
Atau Anda juga bisa menggunakan
String.Format();
Keluaran:
time upon a Once, there was a bar and a foo.
sumber
temp
dari"_"
menjadi<?>
. Tetapi jika diperlukan, apa yang bisa dia lakukan adalah menambahkan parameter lain ke metode yang akan mengubah temp. - "Lebih baik untuk membuatnya tetap sederhana, kan?"Inilah versi saya, yang berbasis kata:
sumber
Cara yang sedikit rumit tetapi Anda perlu melakukan beberapa pemeriksaan lagi.
1.mengonversi string ke array karakter
2.loop on temp dan ganti
foo
denganbar
danbar
denganfoo
karena tidak ada peluang mendapatkan string yang dapat diganti lagi.sumber
Nah, jawaban yang lebih pendek adalah ...
sumber
Dengan menggunakan jawaban yang ditemukan di sini Anda dapat menemukan semua kemunculan string yang ingin Anda ganti.
Jadi misalnya Anda menjalankan kode pada jawaban SO di atas. Buat dua tabel indeks (misalkan bilah dan foo tidak hanya muncul sekali di string Anda) dan Anda bisa bekerja dengan tabel-tabel itu untuk menggantinya di string Anda.
Sekarang untuk mengganti lokasi indeks tertentu yang dapat Anda gunakan:
Sedangkan
pos
indeks di mana string Anda mulai (dari tabel indeks yang saya kutip di atas). Jadi katakanlah Anda membuat dua tabel indeks untuk masing-masing. Mari kita panggil merekaindexBar
danindexFoo
.Sekarang, sebagai gantinya, Anda dapat menjalankan dua loop, satu untuk setiap penggantian yang ingin Anda buat.
Demikian pula loop lain untuk
indexFoo
.Ini mungkin tidak seefisien jawaban lain di sini, tetapi lebih mudah dipahami daripada Peta atau hal lainnya.
Ini akan selalu memberi Anda hasil yang Anda inginkan dan untuk beberapa kemungkinan kemunculan setiap string. Selama Anda menyimpan indeks dari setiap kejadian.
Juga jawaban ini tidak memerlukan rekursi atau ketergantungan eksternal. Sejauh kerumitannya berjalan dengan baik adalah O (n kuadrat), sedangkan n adalah jumlah dari kedua kata tersebut.
sumber
Saya mengembangkan kode ini akan memecahkan masalah:
Dalam penggunaan utama
change(story,word2,word1).
sumber
sumber