Bagaimana cara mengganti ekspresi reguler di MySQL?


516

Saya punya meja dengan ~ 500 k baris; varchar (255) kolom UTF8 filenameberisi nama file;

Saya mencoba menghapus berbagai karakter aneh dari nama file - saya pikir saya akan menggunakan kelas karakter: [^a-zA-Z0-9()_ .\-]

Sekarang, apakah ada fungsi di MySQL yang memungkinkan Anda mengganti melalui ekspresi reguler ? Saya mencari fungsi yang mirip dengan fungsi REPLACE () - contoh sederhana berikut:

SELECT REPLACE('stackowerflow', 'ower', 'over');

Output: "stackoverflow"

/* does something like this exist? */
SELECT X_REG_REPLACE('Stackoverflow','/[A-Zf]/','-'); 

Output: "-tackover-low"

Saya tahu tentang REGEXP / RLIKE , tetapi itu hanya memeriksa apakah ada kecocokan, bukan apa kecocokannya.

(Saya bisa melakukan " SELECT pkey_id,filename FROM foo WHERE filename RLIKE '[^a-zA-Z0-9()_ .\-]'" dari skrip PHP, melakukan a preg_replacelalu " UPDATE foo ... WHERE pkey_id=...", tapi itu seperti peretasan terakhir yang lambat & jelek)


8
Ini permintaan fitur sejak 2007: bugs.mysql.com/bug.php?id=27389 . Jika Anda benar-benar menginginkan fitur ini, masuk dan klik tombol "Mempengaruhi saya". Semoga itu akan mendapatkan suara yang cukup.
TMS

4
@ Thomas: Saya telah melakukan itu ... pada 2009, ketika saya mencari-cari. Karena tidak ada kemajuan di dalamnya - tampaknya itu bukan fitur yang penting. (btw Postgres memilikinya: stackoverflow.com/questions/11722995/... )
Piskvor meninggalkan gedung

1
Terkait, lebih sederhana, versi dari pertanyaan ini: stackoverflow.com/questions/6942973/…
Kzqai

2
Saya telah membuat regexp_split(fungsi + prosedur) & regexp_replace, yang diterapkan dengan REGEXPoperator. Untuk pencarian sederhana, itu akan melakukan trik. Anda dapat menemukannya di sini - jadi, ini adalah cara dengan kode tersimpan MySQL, tanpa UDF. Jika Anda menemukan beberapa bug, yang tidak tercakup oleh batasan yang diketahui - jangan ragu untuk membuka masalah.
Alma Do

1
Menemukan perpustakaan ini dari utas SO lainnya: github.com/mysqludf/lib_mysqludf_preg berfungsi dengan sempurna.
Kyle

Jawaban:


78

Dengan MySQL 8.0+ Anda dapat menggunakan REGEXP_REPLACEfungsi asli.

12.5.2 Ekspresi Reguler :

REGEXP_REPLACE(expr, pat, repl[, pos[, occurrence[, match_type]]])

Menggantikan kejadian dalam string expr yang sesuai dengan ekspresi reguler yang ditentukan oleh pola pat dengan string pengganti repl , dan kembali string yang dihasilkan. Jika expr , tepuk , atau ganti adalah NULL, nilai kembali adalah NULL.

dan dukungan ekspresi reguler :

Sebelumnya, MySQL menggunakan pustaka ekspresi reguler Henry Spencer untuk mendukung operator ekspresi reguler ( REGEXP, RLIKE).

Dukungan ekspresi reguler telah diimplementasikan kembali menggunakan Komponen Internasional untuk Unicode (ICU), yang menyediakan dukungan Unicode penuh dan aman multibyte. The REGEXP_LIKE()Fungsi melakukan pencocokan ekspresi reguler dengan cara dari REGEXPdan RLIKEoperator, yang sekarang adalah sinonim untuk fungsi itu. Selain itu, fungsi REGEXP_INSTR(),, REGEXP_REPLACE()dan REGEXP_SUBSTR() fungsi tersedia untuk menemukan posisi yang cocok dan melakukan subtitusi dan ekstraksi masing-masing.

SELECT REGEXP_REPLACE('Stackoverflow','[A-Zf]','-',1,0,'c'); 
-- Output:
-tackover-low

Demo DBFiddle


147

MySQL 8.0+ :

Anda dapat menggunakan REGEXP_REPLACEfungsi asli .

Versi yang lebih lama:

Anda dapat menggunakan fungsi yang ditentukan pengguna ( UDF ) seperti mysql-udf-regexp .


3
REGEXP_REPLACE sebagai Fungsi Buatan Pengguna? Terlihat menjanjikan, akan memeriksanya. Terima kasih!
Piskvor meninggalkan gedung

15
Sayangnya mysql-udf-regexp tampaknya tidak memiliki dukungan untuk karakter multibyte. regexp_replace ('äöõü', 'ä', '') mengembalikan string numerik yang panjang alih-alih teks asli.
lkraav

3
MySQL sendiri tidak mendukung karakter multi-byte dengan fitur RegEx-nya.
Brad

4
Pengguna Windows: Perpustakaan UDF yang ditautkan di sini sepertinya tidak memiliki dukungan windows yang bagus. Metode instalasi windows yang dijelaskan tidak berfungsi dengan baik untuk saya.
Jonathan

2
@ lkraav Anda harus mencoba pustaka lib_mysqludf_preg di bawah ini karena berfungsi dengan baik. Ini versi verbose karena mengembalikan gumpalan secara default dan saya tidak tahu apakah Anda memiliki charset multibyte sebagai default: pilih cast (TR as char) COLLATE utf8_unicode_ci dari (pilih preg_replace ('/ ä /', '', 'öõüä') R) T
gillyspy

124

Gunakan MariaDB sebagai gantinya. Ini memiliki fungsi

REGEXP_REPLACE(col, regexp, replace)

Lihat MariaDB docs dan PCRE Peningkatan ekspresi reguler

Perhatikan bahwa Anda dapat menggunakan pengelompokan regexp juga (saya menemukan itu sangat berguna):

SELECT REGEXP_REPLACE("stackoverflow", "(stack)(over)(flow)", '\\2 - \\1 - \\3')

kembali

over - stack - flow

12
ini dari mariadb 10
Nick

6
Untuk waktu berikutnya saya membutuhkannya, inilah sintaks untuk mengubah seluruh kolom: UPDATE table SET Name = REGEXP_REPLACE(Name, "-2$", "\\1")Ini menghapus -2 dari abcxyz-2 dari seluruh kolom sekaligus.
Josiah

27
Mengubah seluruh platform bukanlah solusi yang realistis.
David Baucum

3
@DavidBaucum MariaDB adalah pengganti drop-in untuk MySQL. Jadi ini bukan "perubahan platform" tetapi lebih seperti memilih maskapai penerbangan yang berbeda untuk perjalanan yang sama
Benvorth


113

Metode brute force saya untuk mendapatkan ini berfungsi hanya:

  1. Buang meja - mysqldump -u user -p database table > dump.sql
  2. Temukan dan ganti pola pasangan - find /path/to/dump.sql -type f -exec sed -i 's/old_string/new_string/g' {} \;, Ada juga ekspresi perl regeular lainnya yang dapat Anda lakukan pada file.
  3. Impor tabel - mysqlimport -u user -p database table < dump.sql

Jika Anda ingin memastikan string tidak berada di tempat lain dalam dataset Anda, jalankan beberapa ekspresi reguler untuk memastikan semuanya terjadi di lingkungan yang sama. Ini juga tidak sulit untuk membuat cadangan sebelum Anda menjalankan penggantian, jika Anda secara tidak sengaja menghancurkan sesuatu yang kehilangan kedalaman informasi.


33
Oke, itu juga akan berhasil; Saya tidak mempertimbangkan penggantian offline. Nice out-of-the-box berpikir di sana!
Piskvor meninggalkan gedung

10
Sepertinya aneh bagi saya bahwa Anda akan menggunakan find seperti itu, saya akan mempersingkat perintah untuk sed -i 's / old_string / new_string / g' /path/to/dump.sql
speshak

36
Sangat berisiko, dan tidak taktis dengan kumpulan data besar, atau dengan integritas referensial di tempat: untuk menghapus data dan kemudian memasukkannya lagi Anda harus mematikan integritas referensial, meninggalkan dalam prakteknya database Anda juga mati.
Raul Luna

5
Setelah menggunakan metode ini di masa lalu, saya setuju dengan Raul, ini sangat berisiko. Anda harus benar-benar yakin juga, bahwa string Anda tidak ada di dataset Anda.
Eggmatters

1
Bertahun-tahun terlambat untuk jawaban @speshak tetapi alasan saya memilih untuk mengakses file seperti ini adalah karena saya awalnya sangat gugup karena alasan yang sama seperti yang disebutkan di atas. Pada saat itu sepertinya memisahkan bagian "cari file" dari bagian "ganti" akan membuat kode lebih mudah dibaca sebelum saya mengirimkannya
Ryan Ward

42

kami mengatasi masalah ini tanpa menggunakan regex kueri ini hanya mengganti string pencocokan tepat.

update employee set
employee_firstname = 
trim(REPLACE(concat(" ",employee_firstname," "),' jay ',' abc '))

Contoh:

emp_id employee_firstname

1 jay

2 jay ajay

3 jay

Setelah mengeksekusi hasil query:

emp_id employee_firstname

1 abc

2 abc ajay

3 abc


@yellowmelon untuk apa dua pasangan tanda kutip?
codecowboy

5
Dia mengisi nama karyawan dengan spasi sebelum dan sesudah. Hal ini memungkinkan dia untuk mencari-ganti untuk (spasi) nama pegawai (spasi), yang menghindari menangkap nama pegawai "jay" jika bagiannya dari string yang lebih besar "ajay." Lalu dia memangkas ruang saat selesai.
Slam

42

Baru-baru ini saya menulis fungsi MySQL untuk mengganti string menggunakan ekspresi reguler. Anda dapat menemukan posting saya di lokasi berikut:

http://techras.wordpress.com/2011/06/02/regex-replace-for-mysql/

Berikut adalah kode fungsinya:

DELIMITER $$

CREATE FUNCTION  `regex_replace`(pattern VARCHAR(1000),replacement VARCHAR(1000),original VARCHAR(1000))
RETURNS VARCHAR(1000)
DETERMINISTIC
BEGIN 
 DECLARE temp VARCHAR(1000); 
 DECLARE ch VARCHAR(1); 
 DECLARE i INT;
 SET i = 1;
 SET temp = '';
 IF original REGEXP pattern THEN 
  loop_label: LOOP 
   IF i>CHAR_LENGTH(original) THEN
    LEAVE loop_label;  
   END IF;
   SET ch = SUBSTRING(original,i,1);
   IF NOT ch REGEXP pattern THEN
    SET temp = CONCAT(temp,ch);
   ELSE
    SET temp = CONCAT(temp,replacement);
   END IF;
   SET i=i+1;
  END LOOP;
 ELSE
  SET temp = original;
 END IF;
 RETURN temp;
END$$

DELIMITER ;

Contoh eksekusi:

mysql> select regex_replace('[^a-zA-Z0-9\-]','','2my test3_text-to. check \\ my- sql (regular) ,expressions ._,');

25
Saya hanya akan memperkuat poin di atas: fungsi ini menggantikan karakter yang cocok dengan ekspresi satu karakter. Dikatakan di atas bahwa ini digunakan "untuk repalce string menggunakan ekspresi reguler", dan itu bisa sedikit menyesatkan. Itu melakukan tugasnya, tapi itu bukan pekerjaan yang diminta. (Bukan keluhan - itu hanya untuk menyelamatkan orang-orang terkemuka di jalan yang salah)
Jason

2
Akan lebih bermanfaat untuk benar-benar memasukkan kode dalam jawaban Anda alih-alih memposting tautan telanjang.
phobie

2
Bagus - tetapi sayangnya tidak berurusan dengan referensi seperti select regex_replace('.*(abc).*','\1','noabcde')(mengembalikan 'noabcde', bukan 'abc').
Izzy

@ phobie orang lain melakukannya dalam jawaban ini - hanya sebagai referensi jika tautannya mati;)
Izzy

Saya telah memodifikasi metode ini untuk mencoba mengatasi beberapa batasan yang disebutkan di atas dan banyak lagi. Silakan lihat jawaban ini .
Steve Chambers


13

UPDATE 2: Satu set fungsi regex yang berguna termasuk REGEXP_REPLACE sekarang telah disediakan di MySQL 8.0. Ini membuat bacaan tidak perlu kecuali Anda dibatasi untuk menggunakan versi sebelumnya.


UPDATE 1: Sekarang telah membuat ini menjadi posting blog: http://stevettt.blogspot.co.uk/2018/02/a-mysql-regular-expression-replace.html


Berikut ini memperluas fungsi yang disediakan oleh Rasika Godawatte tetapi menjaring semua substring yang diperlukan daripada hanya menguji karakter tunggal:

-- ------------------------------------------------------------------------------------
-- USAGE
-- ------------------------------------------------------------------------------------
-- SELECT reg_replace(<subject>,
--                    <pattern>,
--                    <replacement>,
--                    <greedy>,
--                    <minMatchLen>,
--                    <maxMatchLen>);
-- where:
-- <subject> is the string to look in for doing the replacements
-- <pattern> is the regular expression to match against
-- <replacement> is the replacement string
-- <greedy> is TRUE for greedy matching or FALSE for non-greedy matching
-- <minMatchLen> specifies the minimum match length
-- <maxMatchLen> specifies the maximum match length
-- (minMatchLen and maxMatchLen are used to improve efficiency but are
--  optional and can be set to 0 or NULL if not known/required)
-- Example:
-- SELECT reg_replace(txt, '^[Tt][^ ]* ', 'a', TRUE, 2, 0) FROM tbl;
DROP FUNCTION IF EXISTS reg_replace;
DELIMITER //
CREATE FUNCTION reg_replace(subject VARCHAR(21845), pattern VARCHAR(21845),
  replacement VARCHAR(21845), greedy BOOLEAN, minMatchLen INT, maxMatchLen INT)
RETURNS VARCHAR(21845) DETERMINISTIC BEGIN 
  DECLARE result, subStr, usePattern VARCHAR(21845); 
  DECLARE startPos, prevStartPos, startInc, len, lenInc INT;
  IF subject REGEXP pattern THEN
    SET result = '';
    -- Sanitize input parameter values
    SET minMatchLen = IF(minMatchLen < 1, 1, minMatchLen);
    SET maxMatchLen = IF(maxMatchLen < 1 OR maxMatchLen > CHAR_LENGTH(subject),
                         CHAR_LENGTH(subject), maxMatchLen);
    -- Set the pattern to use to match an entire string rather than part of a string
    SET usePattern = IF (LEFT(pattern, 1) = '^', pattern, CONCAT('^', pattern));
    SET usePattern = IF (RIGHT(pattern, 1) = '$', usePattern, CONCAT(usePattern, '$'));
    -- Set start position to 1 if pattern starts with ^ or doesn't end with $.
    IF LEFT(pattern, 1) = '^' OR RIGHT(pattern, 1) <> '$' THEN
      SET startPos = 1, startInc = 1;
    -- Otherwise (i.e. pattern ends with $ but doesn't start with ^): Set start pos
    -- to the min or max match length from the end (depending on "greedy" flag).
    ELSEIF greedy THEN
      SET startPos = CHAR_LENGTH(subject) - maxMatchLen + 1, startInc = 1;
    ELSE
      SET startPos = CHAR_LENGTH(subject) - minMatchLen + 1, startInc = -1;
    END IF;
    WHILE startPos >= 1 AND startPos <= CHAR_LENGTH(subject)
      AND startPos + minMatchLen - 1 <= CHAR_LENGTH(subject)
      AND !(LEFT(pattern, 1) = '^' AND startPos <> 1)
      AND !(RIGHT(pattern, 1) = '$'
            AND startPos + maxMatchLen - 1 < CHAR_LENGTH(subject)) DO
      -- Set start length to maximum if matching greedily or pattern ends with $.
      -- Otherwise set starting length to the minimum match length.
      IF greedy OR RIGHT(pattern, 1) = '$' THEN
        SET len = LEAST(CHAR_LENGTH(subject) - startPos + 1, maxMatchLen), lenInc = -1;
      ELSE
        SET len = minMatchLen, lenInc = 1;
      END IF;
      SET prevStartPos = startPos;
      lenLoop: WHILE len >= 1 AND len <= maxMatchLen
                 AND startPos + len - 1 <= CHAR_LENGTH(subject)
                 AND !(RIGHT(pattern, 1) = '$' 
                       AND startPos + len - 1 <> CHAR_LENGTH(subject)) DO
        SET subStr = SUBSTRING(subject, startPos, len);
        IF subStr REGEXP usePattern THEN
          SET result = IF(startInc = 1,
                          CONCAT(result, replacement), CONCAT(replacement, result));
          SET startPos = startPos + startInc * len;
          LEAVE lenLoop;
        END IF;
        SET len = len + lenInc;
      END WHILE;
      IF (startPos = prevStartPos) THEN
        SET result = IF(startInc = 1, CONCAT(result, SUBSTRING(subject, startPos, 1)),
                        CONCAT(SUBSTRING(subject, startPos, 1), result));
        SET startPos = startPos + startInc;
      END IF;
    END WHILE;
    IF startInc = 1 AND startPos <= CHAR_LENGTH(subject) THEN
      SET result = CONCAT(result, RIGHT(subject, CHAR_LENGTH(subject) + 1 - startPos));
    ELSEIF startInc = -1 AND startPos >= 1 THEN
      SET result = CONCAT(LEFT(subject, startPos), result);
    END IF;
  ELSE
    SET result = subject;
  END IF;
  RETURN result;
END//
DELIMITER ;

Demo

Demo Rextester

Keterbatasan

  1. Metode ini tentu saja akan memakan waktu ketika string subjek besar. Pembaruan: Sekarang telah menambahkan parameter panjang kecocokan minimum dan maksimum untuk meningkatkan efisiensi ketika diketahui (nol = tidak diketahui / tidak terbatas).
  2. Ini tidak akan memungkinkan penggantian referensi (misalnya \1, \2 dll.) Untuk menggantikan grup penangkap. Jika fungsi ini diperlukan, silakan lihat jawaban ini yang mencoba memberikan solusi dengan memperbarui fungsi untuk memungkinkan pencarian dan penggantian sekunder dalam setiap kecocokan yang ditemukan (dengan mengorbankan kompleksitas yang meningkat).
  3. Jika ^dan / atau $digunakan dalam pola, masing-masing harus di awal dan di akhir - misalnya pola seperti (^start|end$)tidak didukung.
  4. Ada bendera "serakah" untuk menentukan apakah pencocokan keseluruhan harus serakah atau tidak serakah. Menggabungkan kecocokan serakah dan malas dalam satu ekspresi reguler (mis. a.*?b.*) Tidak didukung.

Contoh Penggunaan

Fungsi ini telah digunakan untuk menjawab pertanyaan StackOverflow berikut:


7

Anda 'bisa' melakukannya ... tapi itu tidak terlalu bijaksana ... ini adalah tentang berani seperti saya akan mencoba ... sejauh RegEx penuh mendukung Anda jauh lebih baik menggunakan perl atau sejenisnya.

UPDATE db.tbl
SET column = 
CASE 
WHEN column REGEXP '[[:<:]]WORD_TO_REPLACE[[:>:]]' 
THEN REPLACE(column,'WORD_TO_REPLACE','REPLACEMENT')
END 
WHERE column REGEXP '[[:<:]]WORD_TO_REPLACE[[:>:]]'

1
Tidak, itu tidak akan berhasil. Bayangkan kolom Anda berisi 'asdfWORD_TO_REPLACE WORD_TO_REPLACE ". Metode Anda akan menghasilkan' asdfREPLACEMENT REPLACEMENT" di mana jawaban yang benar adalah "asdfWORD_TO_REPLACE REPLACEMENT".
Ryan Shillington

1
@Ryan ... itulah sebabnya saya menyatakan bahwa itu tidak terlalu bijaksana ... dalam kasus penggunaan yang Anda berikan ini pasti akan gagal. Singkatnya itu ide yang buruk untuk menggunakan struktur 'regex-like'. Lebih buruk lagi ... jika Anda meletakkan klausa di mana semua nilai Anda akan NULL ...
Eddie B

1
Sebenarnya Ryan dalam hal ini Anda salah karena spidol hanya akan menemukan kecocokan untuk 'batas' kata panjang-nol sehingga hanya kata-kata dengan batas sebelum dan sesudah kata akan cocok ... Masih merupakan ide yang buruk ...
Eddie B

6

Kita dapat menggunakan kondisi JIKA dalam permintaan SELECT seperti di bawah ini:

Misalkan untuk apa pun dengan "ABC", "ABC1", "ABC2", "ABC3", ..., kami ingin mengganti dengan "ABC" lalu menggunakan kondisi REGEXP dan IF () dalam permintaan SELECT, kita dapat mencapai ini .

Sintaksis:

SELECT IF(column_name REGEXP 'ABC[0-9]$','ABC',column_name)
FROM table1 
WHERE column_name LIKE 'ABC%';

Contoh:

SELECT IF('ABC1' REGEXP 'ABC[0-9]$','ABC','ABC1');

Halo, terima kasih atas sarannya. Saya telah mencoba sesuatu yang serupa, tetapi kinerja pada set data saya tidak memuaskan. Untuk perangkat bertubuh kecil, ini mungkin layak.
Piskvor meninggalkan gedung

3

Yang di bawah ini pada dasarnya menemukan kecocokan pertama dari kiri dan kemudian menggantikan semua kemunculannya (diuji dalam ).

Pemakaian:

SELECT REGEX_REPLACE('dis ambiguity', 'dis[[:space:]]*ambiguity', 'disambiguity');

Penerapan:

DELIMITER $$
CREATE FUNCTION REGEX_REPLACE(
  var_original VARCHAR(1000),
  var_pattern VARCHAR(1000),
  var_replacement VARCHAR(1000)
  ) RETURNS
    VARCHAR(1000)
  COMMENT 'Based on https://techras.wordpress.com/2011/06/02/regex-replace-for-mysql/'
BEGIN
  DECLARE var_replaced VARCHAR(1000) DEFAULT var_original;
  DECLARE var_leftmost_match VARCHAR(1000) DEFAULT
    REGEX_CAPTURE_LEFTMOST(var_original, var_pattern);
    WHILE var_leftmost_match IS NOT NULL DO
      IF var_replacement <> var_leftmost_match THEN
        SET var_replaced = REPLACE(var_replaced, var_leftmost_match, var_replacement);
        SET var_leftmost_match = REGEX_CAPTURE_LEFTMOST(var_replaced, var_pattern);
        ELSE
          SET var_leftmost_match = NULL;
        END IF;
      END WHILE;
  RETURN var_replaced;
END $$
DELIMITER ;

DELIMITER $$
CREATE FUNCTION REGEX_CAPTURE_LEFTMOST(
  var_original VARCHAR(1000),
  var_pattern VARCHAR(1000)
  ) RETURNS
    VARCHAR(1000)
  COMMENT '
  Captures the leftmost substring that matches the [var_pattern]
  IN [var_original], OR NULL if no match.
  '
BEGIN
  DECLARE var_temp_l VARCHAR(1000);
  DECLARE var_temp_r VARCHAR(1000);
  DECLARE var_left_trim_index INT;
  DECLARE var_right_trim_index INT;
  SET var_left_trim_index = 1;
  SET var_right_trim_index = 1;
  SET var_temp_l = '';
  SET var_temp_r = '';
  WHILE (CHAR_LENGTH(var_original) >= var_left_trim_index) DO
    SET var_temp_l = LEFT(var_original, var_left_trim_index);
    IF var_temp_l REGEXP var_pattern THEN
      WHILE (CHAR_LENGTH(var_temp_l) >= var_right_trim_index) DO
        SET var_temp_r = RIGHT(var_temp_l, var_right_trim_index);
        IF var_temp_r REGEXP var_pattern THEN
          RETURN var_temp_r;
          END IF;
        SET var_right_trim_index = var_right_trim_index + 1;
        END WHILE;
      END IF;
    SET var_left_trim_index = var_left_trim_index + 1;
    END WHILE;
  RETURN NULL;
END $$
DELIMITER ;

3

Saya pikir ada cara mudah untuk mencapai ini dan Ini berfungsi dengan baik untuk saya.

Untuk MEMILIH baris menggunakan REGEX

SELECT * FROM `table_name` WHERE `column_name_to_find` REGEXP 'string-to-find'

Untuk MEMPERBARUI baris menggunakan REGEX

UPDATE `table_name` SET column_name_to_find=REGEXP_REPLACE(column_name_to_find, 'string-to-find', 'string-to-replace') WHERE column_name_to_find REGEXP 'string-to-find'

Referensi REGEXP: https://www.geeksforgeeks.org/mysql-regular-expressions-regexp/


Terima kasih :) Hal ini dimungkinkan dengan mudah sejak versi 8.
Piskvor meninggalkan gedung
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.