Hapus ✅, 🔥, ✈, ♛ dan emoji / gambar / tanda lain dari string Java

192

Saya memiliki beberapa string dengan semua jenis emoji / gambar / tanda yang berbeda di dalamnya.

Tidak semua string dalam bahasa Inggris - beberapa dari mereka dalam bahasa non-Latin lainnya, misalnya:

▓ railway??
→ Cats and dogs
I'm on 🔥
Apples ⚛ 
✅ Vi sign
♛ I'm the king ♛ 
Corée ♦ du Nord ☁  (French)
 gjør at både ◄╗ (Norwegian)
Star me ★
Star ⭐ once more
早上好 ♛ (Chinese)
Καλημέρα ✂ (Greek)
another ✓ sign ✓
добрай раніцы ✪ (Belarus)
◄ शुभ प्रभात ◄ (Hindi)
✪ ✰ ❈ ❧ Let's get together ★. We shall meet at 12/10/2018 10:00 AM at Tony's.❉

... dan masih banyak lagi.

Saya ingin menyingkirkan semua tanda / gambar ini dan hanya menyimpan huruf (dan tanda baca) dalam berbagai bahasa.

Saya mencoba membersihkan tanda-tanda menggunakan perpustakaan EmojiParser :

String withoutEmojis = EmojiParser.removeAllEmojis(input);

Masalahnya adalah EmojiParser tidak dapat menghapus sebagian besar tanda. Tanda ♦ adalah satu-satunya yang saya temukan sampai sekarang setelah dihapus. Tanda-tanda lain seperti ✪ ❉ ★ ✰ ❈ ❧ ✂ ❋ ⓡ ✿ ♛ 🔥 tidak dihapus.

Apakah ada cara untuk menghapus semua tanda-tanda ini dari string input dan hanya menyimpan huruf dan tanda baca dalam bahasa yang berbeda ?

riorio
sumber
91
apa yang ingin kamu simpan?
YCF_L
31
Dua masalah: Apa itu EmojiParser? Tampaknya tidak menjadi bagian dari perpustakaan standar, jadi penyebutan ini tidak terlalu membantu. Dan karakter apa yang ingin Anda filter? Anda mengatakan "lebih banyak dari jenis ini", tetapi ada banyak kelompok karakter dan keluarga. Kami perlu tahu lebih banyak tentang kriteria Anda.
Markus Fischer
129
IDK apa motivasi Anda di balik ini, tetapi jika terlalu filter input teks: jangan. Saya lelah dipaksa menggunakan a-zA-Z. Biarkan saya menulis dalam bahasa ibu saya, atau emoji, atau apa pun yang saya inginkan. Apakah saya benar-benar ingin saya membuat janji temu disebut "🤦🏻‍♂️"? Ya, ya saya lakukan. Sekarang keluarlah dari jalanku.
Alexander - Pasang kembali Monica
19
Harap jelaskan apa yang ingin Anda simpan dan hapus. Di permukaan pertanyaannya tampak jelas tetapi karena kompleksitas Unicode tidak dan karena itu tidak mungkin untuk memberikan jawaban yang baik.
Oleg
12
ini sepertinya hal aneh yang ingin dilakukan ketika itu menghancurkan makna setidaknya satu dari contoh Anda?
Eevee

Jawaban:

290

Alih-alih memasukkan beberapa elemen ke daftar hitam, bagaimana dengan membuat daftar putih karakter yang ingin Anda simpan? Dengan cara ini Anda tidak perlu khawatir tentang setiap emoji baru yang ditambahkan.

String characterFilter = "[^\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\p{Cf}\\p{Cs}\\s]";
String emotionless = aString.replaceAll(characterFilter,"");

Begitu:

  • [\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\p{Cf}\\p{Cs}\\s]adalah rentang yang mewakili semua angka ( \\p{N}), huruf ( \\p{L}), tanda ( \\p{M}), tanda baca ( \\p{P}), spasi putih / pemisah ( \\p{Z}), pemformatan lainnya ( \\p{Cf}) dan karakter lain U+FFFFdi atas dalam karakter Unicode ( \\p{Cs}), dan baris baru ( \\s).\\p{L}khusus mencakup karakter dari huruf lain seperti Sirilik, Latin, Kanji, dll.
  • Itu ^ dalam karakter regex meniadakan pertandingan.

Contoh:

String str = "hello world _# 皆さん、こんにちは! 私はジョンと申します。🔥";
System.out.print(str.replaceAll("[^\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\p{Cf}\\p{Cs}\\s]",""));
// Output:
//   "hello world _# 皆さん、こんにちは! 私はジョンと申します。"

Jika Anda memerlukan informasi lebih lanjut, lihat dokumentasi Java untuk regex.

Nick Bull
sumber
4
Kesenjangan yang jelas antara karakter alfanumerik ASCII dan emoji adalah aksen dan huruf non-latin. Tanpa masukan OP tentang ini, kita tidak tahu apakah ini jawaban yang baik (bukan DV saya)
Chris H
4
Ya saya ingin tahu mengapa ini mungkin akan downvoting. Saat saya melihat pertanyaan ini, ekspresi reguler adalah hal pertama yang mutlak terlintas di benak saya (PS karena dia mencari karakter standar dan tanda baca, saya akan menggunakan sesuatu seperti [^\w\^\-\[\]\.!@#$%&*\(\)/+'":;~?,]tapi itu hanya saya yang kuat dan mencoba mengumpulkan semua karakter khas yang muncul adalah simbol). Terpilih karena ini jelas merupakan solusi potensial. Jika dia ingin menambahkan beberapa karakter bahasa lain, dia dapat menambahkannya ke ekspresi seperlunya.
Chris
15
@ Chris contoh tanda baca yang bagus, terlihat cukup luas bagi saya untuk beberapa kasus. Juga mungkin orang-orang tidak membaca seluruh jawaban saat itu - sebagaimana dinyatakan di bagian bawah jawaban, p{L}menangani karakter alfabet non-Inggris . Saya harap ini dipahami bahwa saya tidak dapat mendaftar secara luas melalui setiap alfabet non-Inggris dalam jawaban saya karena itu akan menjadi tidak praktis.
Nick Bull
12
Ini. Silahkan dan terima kasih. Jangan mencoba untuk melarang karakter yang menyebabkan masalah pada Anda; memutuskan karakter apa yang Anda izinkan dan menyandikannya. Kemudian kode Anda memiliki serangkaian kasus uji yang jelas.
jpmc26
2
Saya sarankan "[^\\p{L}\\p{M}\\p{N}\\p{P}\\p{Z}\\p{Cf}\\s]". Ini memungkinkan kategori umum Surat, Tanda, Nomor, Tanda Baca, Pemisah, dan "Lainnya, Format", serta karakter spasi putih seperti tab dan baris baru.
Sean Van Gorder
81

Saya tidak super ke Java, jadi saya tidak akan mencoba untuk menulis kode contoh inline, tetapi cara saya akan melakukan ini adalah untuk memeriksa apa yang disebut Unicode "kategori umum" dari setiap karakter. Ada beberapa huruf dan kategori tanda baca.

Anda dapat menggunakan Character.getType untuk menemukan kategori umum dari karakter yang diberikan. Anda mungkin harus mempertahankan karakter yang termasuk dalam kategori umum ini:

COMBINING_SPACING_MARK
CONNECTOR_PUNCTUATION
CURRENCY_SYMBOL
DASH_PUNCTUATION
DECIMAL_DIGIT_NUMBER
ENCLOSING_MARK
END_PUNCTUATION
FINAL_QUOTE_PUNCTUATION
FORMAT
INITIAL_QUOTE_PUNCTUATION
LETTER_NUMBER
LINE_SEPARATOR
LOWERCASE_LETTER
MATH_SYMBOL
MODIFIER_LETTER
MODIFIER_SYMBOL
NON_SPACING_MARK
OTHER_LETTER
OTHER_NUMBER
OTHER_PUNCTUATION
PARAGRAPH_SEPARATOR
SPACE_SEPARATOR
START_PUNCTUATION
TITLECASE_LETTER
UPPERCASE_LETTER

(Semua karakter yang Anda daftarkan secara spesifik ingin dihapus memiliki kategori umum OTHER_SYMBOL, yang tidak saya sertakan dalam daftar putih kategori di atas.)

Daniel Wagner
sumber
1
FORMAT (Cf) harus dilestarikan juga; ini termasuk pengelompokan pengelompokan dan arah, yang tanpanya tidak mungkin untuk menulis kata-kata tertentu (tidak biasa, diakui) dalam beberapa bahasa.
zwol
@ zwol Terima kasih atas detailnya! Saya akan menambahkannya ke daftar.
Daniel Wagner
29
Ini adalah jawaban untuk masa depan. Terlepas dari pembaruan masa depan untuk standar Unicode, termasuk / tidak termasuk karakter berdasarkan kategorinya berarti bahwa penguraian karakter secara individu dan pemeliharaan daftar tidak diperlukan. Tentu saja, pengujian teks secara sepintas dalam bahasa yang berbeda (misalnya Cina, Arab, dll.) Harus dilakukan untuk memastikan bahwa kategori yang disaring cocok dengan teks yang diperlukan untuk diizinkan di lingkungan target.
CJBS
3
Oh, gotcha lain yang seharusnya saya pikirkan kemarin: TAB, CR, dan LF semuanya adalah kategori umum Cc (Java's CONTROL). Mereka perlu masuk daftar putih khusus, karena Anda hampir pasti tidak ingin mengizinkan sebagian besar karakter kontrol warisan.
zwol
@ CJBS Masalah dengan pendekatan ini adalah bahwa itu hanya sebagian diimplementasikan di Jawa. Misalnya, Character.getType()tidak akan memberi tahu Anda apakah char(atau inttitik kode Anda karena metode ini kelebihan beban) adalah, katakanlah, sebuah emotikon, atau simbol musik, atau karakter emoji, dll. Jika Anda memiliki kasus penggunaan yang sederhana mungkin akan baik-baik saja untuk turun ke jalan ini - ini tentu saja merupakan pendekatan elegan yang mudah dipahami - tetapi perlu diketahui bahwa itu mungkin rusak jika persyaratan berubah.
skomisa
47

Berdasarkan Daftar Emoji Lengkap, v11.0 Anda memiliki 1644 poin kode Unicode yang berbeda untuk dihapus. Misalnya ada di daftar ini sebagai U+2705.

Memiliki daftar lengkap emoji, Anda perlu memfilternya menggunakan poin kode . Iterasi lebih dari satu charatau bytetidak akan berfungsi sebagai titik kode tunggal dapat menjangkau beberapa byte. Karena Java menggunakan UTF-16 emoji biasanya akan memakan waktu dua chardetik.

String input = "ab✅cd";
for (int i = 0; i < input.length();) {
  int cp = input.codePointAt(i);
  // filter out if matches
  i += Character.charCount(cp); 
}

Memetakan dari titik kode Unicode U+2705ke Jawa intsecara langsung:

int viSign = 0x2705;

atau karena Java mendukung Unicode Strings:

int viSign = "✅".codePointAt(0);
Karol Dowbecki
sumber
28
Daftar yang sangat berguna. Menarik bahwa sesuatu yang disebut EmojiParser dengan metode yang disebut removeAllEmojis gagal menangani ini ... :-)
TJ Crowder
7
@Bergi: Tidak, karena input.codePointAthanya melihat paling banyak 2 karakter yang merupakan batas atas yang konstan. Juga (yang baru ditambahkan) i += Character.charCount(cp)melompati semua karakter yang input.codePointAtdiperiksa (minus 1 dalam beberapa kasus sudut).
David Foerster
6
@ OlivierGrégoire: String.chars()stream lebih dari karakter bukan codepoint. Ada metode terpisah String.codePoints()untuk itu.
David Foerster
5
Setidaknya ada dua masalah di sini: Anda menggunakan daftar emoji "tertutup", jadi setiap tahun Anda harus memperpanjangnya (tetapi ini mungkin tidak mudah dipecahkan), dan kode ini mungkin tidak akan bekerja dengan benar dengan urutan codepoints (lihat misalnya unicode.org/Public/emoji/11.0/emoji-zwj- berikutnyaences.txt )
xanatos
49
Ini pada dasarnya adalah pendekatan yang sama seperti yang digunakan oleh EmojiParser dan akan segera gagal karena alasan yang sama. Emoji baru relatif sering ditambahkan ke basis data karakter Unicode dan jika Anda sekarang menerapkan solusi menggunakan emoji 1644 yang saat ini didefinisikan untuk set aturan negatif, implementasi akan gagal segera setelah emoji baru tersedia.
jarnbjo
20

ICU4J adalah temanmu.

UCharacter.hasBinaryProperty(UProperty.EMOJI);

Ingatlah untuk selalu memperbarui versi icu4j Anda dan perhatikan ini hanya akan menyaring emoji Unicode resmi, bukan karakter simbol. Gabungkan dengan menyaring tipe karakter lain yang diinginkan.

Informasi lebih lanjut: http://icu-project.org/apiref/icu4j/com/ibm/icu/lang/UProperty.html#EMOJI

Daniel F
sumber
1
Sampai Java diperbarui untuk memasukkan properti biner Emoji, saya kira ini akan menjadi solusi yang baik. Perpustakaan perlu sering diperbarui untuk codepoint yang baru ditambahkan.
nhahtdh
10

Saya memberikan beberapa contoh di bawah ini, dan berpikir bahwa bahasa Latin sudah cukup, tetapi ...

Apakah ada cara untuk menghapus semua tanda-tanda ini dari string input dan hanya menyimpan huruf & tanda baca dalam bahasa yang berbeda?

Setelah mengedit, mengembangkan solusi baru, menggunakan Character.getTypemetode, dan yang tampaknya menjadi cara terbaik untuk ini.

package zmarcos.emoji;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class TestEmoji {

    public static void main(String[] args) {
        String[] arr = {"Remove ✅, 🔥, ✈ , ♛ and other such signs from Java string",
            "→ Cats and dogs",
            "I'm on 🔥",
            "Apples ⚛ ",
            "✅ Vi sign",
            "♛ I'm the king ♛ ",
            "Star me ★",
            "Star ⭐ once more",
            "早上好 ♛",
            "Καλημέρα ✂"};
        System.out.println("---only letters and spaces alike---\n");
        for (String input : arr) {
            int[] filtered = input.codePoints().filter((cp) -> Character.isLetter(cp) || Character.isWhitespace(cp)).toArray();
            String result = new String(filtered, 0, filtered.length);
            System.out.println(input);
            System.out.println(result);
        }

        System.out.println("\n---unicode blocks white---\n");
        Set<Character.UnicodeBlock> whiteList = new HashSet<>();
        whiteList.add(Character.UnicodeBlock.BASIC_LATIN);
        for (String input : arr) {
            int[] filtered = input.codePoints().filter((cp) -> whiteList.contains(Character.UnicodeBlock.of(cp))).toArray();
            String result = new String(filtered, 0, filtered.length);
            System.out.println(input);
            System.out.println(result);
        }

        System.out.println("\n---unicode blocks black---\n");
        Set<Character.UnicodeBlock> blackList = new HashSet<>();        
        blackList.add(Character.UnicodeBlock.EMOTICONS);
        blackList.add(Character.UnicodeBlock.MISCELLANEOUS_TECHNICAL);
        blackList.add(Character.UnicodeBlock.MISCELLANEOUS_SYMBOLS);
        blackList.add(Character.UnicodeBlock.MISCELLANEOUS_SYMBOLS_AND_ARROWS);
        blackList.add(Character.UnicodeBlock.MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS);
        blackList.add(Character.UnicodeBlock.ALCHEMICAL_SYMBOLS);
        blackList.add(Character.UnicodeBlock.TRANSPORT_AND_MAP_SYMBOLS);
        blackList.add(Character.UnicodeBlock.GEOMETRIC_SHAPES);
        blackList.add(Character.UnicodeBlock.DINGBATS);
        for (String input : arr) {
            int[] filtered = input.codePoints().filter((cp) -> !blackList.contains(Character.UnicodeBlock.of(cp))).toArray();
            String result = new String(filtered, 0, filtered.length);
            System.out.println(input);
            System.out.println(result);
        }
        System.out.println("\n---category---\n");
        int[] category = {Character.COMBINING_SPACING_MARK, Character.COMBINING_SPACING_MARK, Character.CONNECTOR_PUNCTUATION, /*Character.CONTROL,*/ Character.CURRENCY_SYMBOL,
            Character.DASH_PUNCTUATION, Character.DECIMAL_DIGIT_NUMBER, Character.ENCLOSING_MARK, Character.END_PUNCTUATION, Character.FINAL_QUOTE_PUNCTUATION,
            /*Character.FORMAT,*/ Character.INITIAL_QUOTE_PUNCTUATION, Character.LETTER_NUMBER, Character.LINE_SEPARATOR, Character.LOWERCASE_LETTER,
            /*Character.MATH_SYMBOL,*/ Character.MODIFIER_LETTER, /*Character.MODIFIER_SYMBOL,*/ Character.NON_SPACING_MARK, Character.OTHER_LETTER, Character.OTHER_NUMBER,
            Character.OTHER_PUNCTUATION, /*Character.OTHER_SYMBOL,*/ Character.PARAGRAPH_SEPARATOR, /*Character.PRIVATE_USE,*/
            Character.SPACE_SEPARATOR, Character.START_PUNCTUATION, /*Character.SURROGATE,*/ Character.TITLECASE_LETTER, /*Character.UNASSIGNED,*/ Character.UPPERCASE_LETTER};
        Arrays.sort(category);
        for (String input : arr) {
            int[] filtered = input.codePoints().filter((cp) -> Arrays.binarySearch(category, Character.getType(cp)) >= 0).toArray();
            String result = new String(filtered, 0, filtered.length);
            System.out.println(input);
            System.out.println(result);
        }
    }

}

Keluaran:

---only letters and spaces alike---

Remove ✅, 🔥,  ,  and other such signs from Java string
Remove      and other such signs from Java string
 Cats and dogs
 Cats and dogs
I'm on 🔥
Im on 
Apples  
Apples  
 Vi sign
 Vi sign
 I'm the king  
 Im the king  
Star me 
Star me 
Star  once more
Star  once more
早上好 
早上好 
Καλημέρα 
Καλημέρα 

---unicode blocks white---

Remove ✅, 🔥,  ,  and other such signs from Java string
Remove , ,  ,  and other such signs from Java string
 Cats and dogs
 Cats and dogs
I'm on 🔥
I'm on 
Apples  
Apples  
 Vi sign
 Vi sign
 I'm the king  
 I'm the king  
Star me 
Star me 
Star  once more
Star  once more
早上好 

Καλημέρα 


---unicode blocks black---

Remove ✅, 🔥,  ,  and other such signs from Java string
Remove , ,  ,  and other such signs from Java string
 Cats and dogs
 Cats and dogs
I'm on 🔥
I'm on 
Apples  
Apples  
 Vi sign
 Vi sign
 I'm the king  
 I'm the king  
Star me 
Star me 
Star  once more
Star  once more
早上好 
早上好 
Καλημέρα 
Καλημέρα 

---category---

Remove ✅, 🔥,  ,  and other such signs from Java string
Remove , ,  ,  and other such signs from Java string
 Cats and dogs
 Cats and dogs
I'm on 🔥
I'm on 
Apples  
Apples  
 Vi sign
 Vi sign
 I'm the king  
 I'm the king  
Star me 
Star me 
Star  once more
Star  once more
早上好 
早上好 
Καλημέρα 
Καλημέρα 

Kode berfungsi dengan mengalirkan String ke titik-kode. Kemudian menggunakan lambdas untuk memfilter karakter ke dalam intarray, lalu kita mengubah array menjadi String.

The huruf dan spasi menggunakan menggunakan metode Karakter untuk filter, tidak baik dengan tanda baca. Upaya gagal .

The blok unicode putih filter menggunakan blok unicode yang menspesifikasikan programmer sebagaimana diizinkan. Upaya gagal .

The blok unicode hitam filter menggunakan blok unicode yang menspesifikasikan programmer tidak diperbolehkan. Upaya gagal .

The kategori filter menggunakan metode statis Character.getType. Programmer dapat mendefinisikan dalam categoryarray tipe apa yang diperbolehkan. KARYA 😨😱😰😲😀.

Marcos Zolnowski
sumber
import java.lang.Character.UnicodeBlock;, lalu Character.UnicodeBlock-> UnicodeBlock.
Bernhard Barker
Semua cara Anda gagal dalam tes.
Oleg
@Oleg tidak, lihat lagi, white listcontohnya.
Marcos Zolnowski
Pasti ada yang salah dengan mata atau monitor saya, saya tidak bisa melihat 早上 好 dan Καλημέρα
Oleg
4
Perhatikan bahwa bahasa Java sedikit lambat mendukung versi Unicode yang lebih baru ... Misalnya Java 10 hanya mendukung Unicode 8 (sehingga kelas karakternya hanya menggambarkan Unicode 8 karakter) ... Begitu banyak emoji yang tidak ada (lihat docs.oracle .com / javase / 10 / docs / api / java / lang / Character.html , Informasi karakter didasarkan pada Standar Unicode, versi 8.0.0. )
xanatos
0

Coba proyek ini simple-emoji-4j

Kompatibel dengan Emoji 12.0 (2018.10.15)

Sederhana dengan:

EmojiUtils.removeEmoji(str)
liheyuan
sumber
-1

Gunakan plugin jQuery yang disebut RM-Emoji. Begini cara kerjanya:

$('#text').remove('emoji').fast()

Ini adalah mode cepat yang mungkin melewatkan beberapa emoji karena menggunakan algoritma heuristik untuk menemukan emoji dalam teks. Gunakan .full()metode ini untuk memindai seluruh string dan menghapus semua emoji yang dijamin.

Atwood Mandelbrot-Spolsky
sumber
5
Pertanyaannya adalah di Jawa, maka plugin jQuery tidak relevan di sini.
riorio