Bagaimana cara menyalin file dengan Python?


2478

Bagaimana cara menyalin file dengan Python?

Saya tidak dapat menemukan apa pun di bawah os.


117
Tampaknya cp bukan panggilan sistem dan karenanya bukan milik modul os. Ini adalah perintah shell, jadi dimasukkan ke dalam modul shutil.
waldol1

Jawaban:


3011

shutilmemiliki banyak metode yang dapat Anda gunakan. Salah satunya adalah:

from shutil import copyfile
copyfile(src, dst)
  • Salin konten file bernama src ke file bernama dst .
  • Lokasi tujuan harus dapat ditulis; jika tidak, pengecualian IOError akan dimunculkan.
  • Jika dst sudah ada, itu akan diganti.
  • File khusus seperti karakter atau perangkat blok dan pipa tidak dapat disalin dengan fungsi ini.
  • Dengan copy , src dan dst adalah nama path yang diberikan sebagai string .

Jika Anda menggunakan os.pathoperasi, gunakan copydaripada copyfile. hanyacopyfile akan menerima string .


147
Apa perbedaan antara menyalin dan menyalin?
Matt

387
dalam copy (src, dst) dst dapat berupa direktori.
Owen

41
Perhatikan bahwa tidak semua metadata akan disalin, tergantung pada platform Anda.
Kevin Horn

12
Perhatikan bahwa ini bukan operasi atom. Berhati-hatilah menggunakannya dalam aplikasi berulir.
waterbyte

4
Perhatikan bahwa itu tidak bisa menangani singkatan seperti ~, tetapi dapat menangani jalur relatif
zwep

1253
┌──────────────────┬────────┬───────────┬───────┬────────────────┐
│     Function     │ Copies │   Copies  │Can use│   Destination  │
│                  │metadata│permissions│buffer │may be directory│
├──────────────────┼────────┼───────────┼───────┼────────────────┤
│shutil.copy       │   No   │    Yes    │   No  │      Yes       │
│shutil.copyfile   │   No   │     No    │   No  │       No       │
│shutil.copy2      │  Yes   │    Yes    │   No  │      Yes       │
│shutil.copyfileobj│   No   │     No    │  Yes  │       No       │
└──────────────────┴────────┴───────────┴───────┴────────────────┘

732

copy2(src,dst)seringkali lebih bermanfaat daripada copyfile(src,dst)karena:

  • itu memungkinkan dstuntuk menjadi direktori (bukan nama file target lengkap), dalam hal ini nama dasar dari srcdigunakan untuk membuat file baru;
  • itu mempertahankan modifikasi asli dan info akses (mtime dan atime) dalam metadata file (namun, ini datang dengan sedikit overhead).

Ini adalah contoh singkat:

import shutil
shutil.copy2('/src/dir/file.ext', '/dst/dir/newname.ext') # complete target filename given
shutil.copy2('/src/file.ext', '/dst/dir') # target filename is /dst/dir/file.ext

19
Saya mencoba menyalin secara acak 100 ribu file dari 1 juta file. copyfilejauh lebih cepat daripadacopy2
Vijay

4
apakah saya benar untuk menganggap bahwa shutil.copy2('/dir/file.ext', '/new/dir/')(dengan garis miring setelah jalur target) akan menghapus ambiguitas mengenai apakah akan menyalin ke file baru yang disebut "dir" atau untuk memasukkan file ke direktori nama itu?
Zak

1
@ Vijay, saya yakin overhead ini karena menyalin metadata.
Jonathan H

@ Zak Tidak ada ambiguitas jika /new/dirada direktori yang ada, lihat komentar @ MatthewAlpert.
Jonathan H

@ Zak Anda benar, menambahkan garis miring pada akhirnya menghilangkan ambiguitas. Jika /new/dir/tidak ada, Python akan melempar IsADirectoryError, jika tidak, menyalin file ke /new/dir/bawah nama asli.
martonbognar

125

Anda dapat menggunakan salah satu fungsi salin dari shutilpaket:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Fungsi mempertahankan mendukung menerima salinan lainnya
                      direktori hak akses. mengajukan keberatan metadata  
―――――――――――――――――――――――――――――――――――――――――――――――――― ――――――――――――――――――――――――――――
shutil.copy               ✔ ✔ ☐ ☐
 shutil.copy2              ✔ ✔ ☐ ✔
 shutil.copyfile           ☐ ☐ ☐ ☐
 shutil.copyfileobj        ☐ ☐ ✔ ☐
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Contoh:

import shutil
shutil.copy('/etc/hostname', '/var/tmp/testhostname')

10
Hanya ingin tahu, bagaimana Anda menghasilkan tabel itu?
lightalchemist

16
@lightalchemist Saya baru saja menggunakan vim sebagai scratchpad, menyalin simbol unicode yang digunakan dari tabel wikipedia dan menyalin hasilnya ke editor stackoverflow untuk pemolesan akhir.
maxschlepzig

3
Bagaimana ini berbeda dari jawaban lain 2 tahun sebelumnya? stackoverflow.com/a/30359308/674039
wim

1
@wim, Anda harus membandingkan jawaban saya dengan versi 2017 dari jawaban yang Anda tautkan saat ini ketika saya memposting jawaban saya. Perbedaan utama: jawaban saya menggunakan tajuk kolom yang lebih baik / lebih deskriptif, tata letak tabel tidak mengganggu, itu termasuk tautan langsung ke dalam dokumentasi dan saya menambahkan kolom (yaitu 'menerima file obj').
maxschlepzig

4
BAIK. YMMV, tapi saya pikir perubahan kosmetik dan perbaikan kecil seperti itu lebih baik dilakukan sebagai pengeditan pada jawaban yang ada, daripada duplikasi jawaban.
wim

104

Dengan Python, Anda dapat menyalin file menggunakan


import os
import shutil
import subprocess

1) Menyalin file menggunakan shutilmodul

shutil.copyfile tanda tangan

shutil.copyfile(src_file, dest_file, *, follow_symlinks=True)

# example    
shutil.copyfile('source.txt', 'destination.txt')

shutil.copy tanda tangan

shutil.copy(src_file, dest_file, *, follow_symlinks=True)

# example
shutil.copy('source.txt', 'destination.txt')

shutil.copy2 tanda tangan

shutil.copy2(src_file, dest_file, *, follow_symlinks=True)

# example
shutil.copy2('source.txt', 'destination.txt')  

shutil.copyfileobj tanda tangan

shutil.copyfileobj(src_file_object, dest_file_object[, length])

# example
file_src = 'source.txt'  
f_src = open(file_src, 'rb')

file_dest = 'destination.txt'  
f_dest = open(file_dest, 'wb')

shutil.copyfileobj(f_src, f_dest)  

2) Menyalin file menggunakan osmodul

os.popen tanda tangan

os.popen(cmd[, mode[, bufsize]])

# example
# In Unix/Linux
os.popen('cp source.txt destination.txt') 

# In Windows
os.popen('copy source.txt destination.txt')

os.system tanda tangan

os.system(command)


# In Linux/Unix
os.system('cp source.txt destination.txt')  

# In Windows
os.system('copy source.txt destination.txt')

3) Menyalin file menggunakan subprocessmodul

subprocess.call tanda tangan

subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False)

# example (WARNING: setting `shell=True` might be a security-risk)
# In Linux/Unix
status = subprocess.call('cp source.txt destination.txt', shell=True) 

# In Windows
status = subprocess.call('copy source.txt destination.txt', shell=True)

subprocess.check_output tanda tangan

subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False)

# example (WARNING: setting `shell=True` might be a security-risk)
# In Linux/Unix
status = subprocess.check_output('cp source.txt destination.txt', shell=True)

# In Windows
status = subprocess.check_output('copy source.txt destination.txt', shell=True)


9
Menggunakan perintah single-string adalah gaya pengkodean yang buruk (fleksibilitas, keandalan dan keamanan), alih-alih menggunakan ['copy', sourcefile, destfile]sintaksis sedapat mungkin, terutama jika parameternya dari input pengguna.
Marcel Waldvogel

8
Mengapa Anda daftar begitu banyak alternatif buruk untuk fungsi salin shutil?
maxschlepzig

6
shutil built-in, tidak perlu menyediakan alternatif non-portabel. Jawabannya dapat benar-benar ditingkatkan dengan menghapus solusi yang bergantung pada sistem, dan setelah penghapusan itu, jawaban ini hanyalah salinan dari jawaban yang ada / salinan dokumentasi.
Jean-François Fabre

3
os.popensudah ditinggalkan untuk sementara waktu sekarang. dan check_outputtidak mengembalikan status tetapi output (yang kosong dalam kasus copy/cp)
Jean-François Fabre

2
shutil sebenarnya tidak menyalin file. Ada peringatan besar di bagian atas dokumen . "Ini berarti pemilik file dan grup hilang serta ACL. Pada Mac OS, garpu sumber daya dan metadata lainnya tidak digunakan. Ini berarti bahwa sumber daya akan hilang dan jenis file dan kode pembuat tidak akan benar. Pada Windows, pemilik file, ACL, dan aliran data alternatif tidak disalin. "
GM

96

Menyalin file adalah operasi yang relatif mudah seperti yang ditunjukkan oleh contoh di bawah ini, tetapi Anda sebaiknya menggunakan modul shutil stdlib untuk itu.

def copyfileobj_example(source, dest, buffer_size=1024*1024):
    """      
    Copy a file from source to dest. source and dest
    must be file-like objects, i.e. any object with a read or
    write method, like for example StringIO.
    """
    while True:
        copy_buffer = source.read(buffer_size)
        if not copy_buffer:
            break
        dest.write(copy_buffer)

Jika Anda ingin menyalin dengan nama file Anda bisa melakukan sesuatu seperti ini:

def copyfile_example(source, dest):
    # Beware, this example does not handle any edge cases!
    with open(source, 'rb') as src, open(dest, 'wb') as dst:
        copyfileobj_example(src, dst)

25
Saya melihat beberapa waktu lalu bahwa modul disebut shutil (tunggal) dan tidak shutils (jamak), dan memang itu adalah Python 2.3. Namun demikian saya meninggalkan fungsi ini di sini sebagai contoh.
pi.

4
Menyalin isi file adalah operasi yang mudah. Menyalin file dengan metadata-nya sama sekali tidak langsung, terlebih lagi jika Anda ingin cross-platform.
LaC

3
Benar. Melihat dokumen shutil, fungsi copyfile juga tidak akan menyalin metadata.
pi.

3
Ya, saya tidak yakin mengapa Anda tidak hanya menyalin sumber shutil.copyfileobj. Selain itu, Anda tidak perlu try, finallymenangani penutupan file setelah pengecualian. Saya akan mengatakan, bahwa fungsi Anda seharusnya tidak bertanggung jawab untuk membuka dan menutup file sama sekali. Itu harus masuk dalam fungsi pembungkus, seperti bagaimana shutil.copyfilemembungkus shutil.copyfileobj.
ErlVolton

2
Kode di atas harus menentukan destdapat ditulisi:open(dest, 'wb')
user1016274

69

Gunakan modul shutil .

copyfile(src, dst)

Salin konten file bernama src ke file bernama dst. Lokasi tujuan harus dapat ditulis; jika tidak, pengecualian IOError akan dimunculkan. Jika dst sudah ada, itu akan diganti. File khusus seperti karakter atau perangkat blok dan pipa tidak dapat disalin dengan fungsi ini. src dan dst adalah nama path yang diberikan sebagai string.

Lihatlah filesys untuk semua fungsi penanganan file dan direktori yang tersedia dalam modul Python standar.


shutil sebenarnya tidak menyalin file. Ada peringatan besar di bagian atas dokumen . "Ini berarti pemilik file dan grup hilang serta ACL. Pada Mac OS, garpu sumber daya dan metadata lainnya tidak digunakan. Ini berarti bahwa sumber daya akan hilang dan jenis file dan kode pembuat tidak akan benar. Pada Windows, pemilik file, ACL, dan aliran data alternatif tidak disalin. "
GM

47

Contoh Direktori dan Salin File - Dari Barang Python Tim Golden:

http://timgolden.me.uk/python/win32_how_do_i/copy-a-file.html

import os
import shutil
import tempfile

filename1 = tempfile.mktemp (".txt")
open (filename1, "w").close ()
filename2 = filename1 + ".copy"
print filename1, "=>", filename2

shutil.copy (filename1, filename2)

if os.path.isfile (filename2): print "Success"

dirname1 = tempfile.mktemp (".dir")
os.mkdir (dirname1)
dirname2 = dirname1 + ".copy"
print dirname1, "=>", dirname2

shutil.copytree (dirname1, dirname2)

if os.path.isdir (dirname2): print "Success"

24

Pertama, saya membuat cheatsheet lengkap tentang metode shutil untuk referensi Anda.

shutil_methods =
{'copy':['shutil.copyfileobj',
          'shutil.copyfile',
          'shutil.copymode',
          'shutil.copystat',
          'shutil.copy',
          'shutil.copy2',
          'shutil.copytree',],
 'move':['shutil.rmtree',
         'shutil.move',],
 'exception': ['exception shutil.SameFileError',
                 'exception shutil.Error'],
 'others':['shutil.disk_usage',
             'shutil.chown',
             'shutil.which',
             'shutil.ignore_patterns',]
}

Kedua, jelaskan metode penyalinan dalam exmaples:

  1. shutil.copyfileobj(fsrc, fdst[, length]) memanipulasi objek yang terbuka
In [3]: src = '~/Documents/Head+First+SQL.pdf'
In [4]: dst = '~/desktop'
In [5]: shutil.copyfileobj(src, dst)
AttributeError: 'str' object has no attribute 'read'
#copy the file object
In [7]: with open(src, 'rb') as f1,open(os.path.join(dst,'test.pdf'), 'wb') as f2:
    ...:      shutil.copyfileobj(f1, f2)
In [8]: os.stat(os.path.join(dst,'test.pdf'))
Out[8]: os.stat_result(st_mode=33188, st_ino=8598319475, st_dev=16777220, st_nlink=1, st_uid=501, st_gid=20, st_size=13507926, st_atime=1516067347, st_mtime=1516067335, st_ctime=1516067345)
  1. shutil.copyfile(src, dst, *, follow_symlinks=True) Salin dan ganti nama
In [9]: shutil.copyfile(src, dst)
IsADirectoryError: [Errno 21] Is a directory: ~/desktop'
#so dst should be a filename instead of a directory name
  1. shutil.copy() Salin tanpa membuat metadata
In [10]: shutil.copy(src, dst)
Out[10]: ~/desktop/Head+First+SQL.pdf'
#check their metadata
In [25]: os.stat(src)
Out[25]: os.stat_result(st_mode=33188, st_ino=597749, st_dev=16777220, st_nlink=1, st_uid=501, st_gid=20, st_size=13507926, st_atime=1516066425, st_mtime=1493698739, st_ctime=1514871215)
In [26]: os.stat(os.path.join(dst, 'Head+First+SQL.pdf'))
Out[26]: os.stat_result(st_mode=33188, st_ino=8598313736, st_dev=16777220, st_nlink=1, st_uid=501, st_gid=20, st_size=13507926, st_atime=1516066427, st_mtime=1516066425, st_ctime=1516066425)
# st_atime,st_mtime,st_ctime changed
  1. shutil.copy2() Salin dengan praduga metadata
In [30]: shutil.copy2(src, dst)
Out[30]: ~/desktop/Head+First+SQL.pdf'
In [31]: os.stat(src)
Out[31]: os.stat_result(st_mode=33188, st_ino=597749, st_dev=16777220, st_nlink=1, st_uid=501, st_gid=20, st_size=13507926, st_atime=1516067055, st_mtime=1493698739, st_ctime=1514871215)
In [32]: os.stat(os.path.join(dst, 'Head+First+SQL.pdf'))
Out[32]: os.stat_result(st_mode=33188, st_ino=8598313736, st_dev=16777220, st_nlink=1, st_uid=501, st_gid=20, st_size=13507926, st_atime=1516067063, st_mtime=1493698739, st_ctime=1516067055)
# Preseved st_mtime
  1. shutil.copytree()

Secara rekursif menyalin seluruh pohon direktori yang di-root di src, mengembalikan direktori tujuan


1
shutil sebenarnya tidak menyalin file. Ada peringatan besar di bagian atas dokumen . "Ini berarti pemilik file dan grup hilang serta ACL. Pada Mac OS, garpu sumber daya dan metadata lainnya tidak digunakan. Ini berarti bahwa sumber daya akan hilang dan jenis file dan kode pembuat tidak akan benar. Pada Windows, pemilik file, ACL, dan aliran data alternatif tidak disalin. "
GM

19

Untuk file kecil dan hanya menggunakan built-in python, Anda dapat menggunakan one-liner berikut:

with open(source, 'rb') as src, open(dest, 'wb') as dst: dst.write(src.read())

Seperti @maxschlepzig disebutkan dalam komentar di bawah, ini bukan cara yang optimal untuk aplikasi di mana file terlalu besar atau ketika memori kritis, sehingga jawaban Swati harus lebih disukai.


3
Ini membaca file sumber lengkap ke dalam memori sebelum menulis kembali. Dengan demikian, ini tidak perlu menghabiskan memori untuk semua kecuali operasi penyalinan file terkecil.
maxschlepzig

1
Benarkah? Saya pikir .read()dan .write()buffered secara default (setidaknya untuk CPython).
soundstripe

@soundstripe, Tentu saja ini benar. Fakta bahwa objek file yang dikembalikan oleh open()buffer IO, secara default tidak membantu Anda di sini, karena read()ditentukan sebagai: 'Jika n negatif atau dihilangkan, baca sampai EOF.' Itu berarti read()mengembalikan konten file lengkap sebagai string.
maxschlepzig

@maxschlepzig Saya mengerti maksud Anda dan saya akui saya tidak menyadarinya. Alasan saya memberikan jawaban ini adalah jika seseorang ingin melakukan salinan file sederhana hanya menggunakan built-in, tanpa perlu mengimpor modul untuk itu. Tentu saja pengoptimalan memori seharusnya tidak menjadi masalah jika Anda menginginkan opsi ini. Bagaimanapun terima kasih telah membersihkannya. Saya memperbarui jawabannya.
yellow01

14

Anda bisa menggunakannya os.system('cp nameoffilegeneratedbyprogram /otherdirectory/')

atau seperti yang saya lakukan,

os.system('cp '+ rawfile + ' rawdata.dat')

di mana rawfilenama yang saya buat di dalam program.

Ini adalah solusi hanya Linux


10
ini tidak portabel, dan tidak perlu karena Anda bisa menggunakan shutil.
Corey Goldberg

4
Bahkan ketika shutiltidak tersedia - subprocess.run() (tanpa shell=True!) Adalah alternatif yang lebih baik os.system().
maxschlepzig

1
shutil lebih portabel
Hiadore

1
subprocess.run()seperti yang disarankan oleh @maxschlepzig adalah langkah besar ke depan, saat memanggil program eksternal. Namun untuk fleksibilitas dan keamanan, gunakan ['cp', rawfile, 'rawdata.dat']bentuk melewati baris perintah. (Namun, untuk menyalin, shutildan teman-teman disarankan untuk tidak memanggil program eksternal.)
Marcel Waldvogel

2
coba itu dengan nama file dengan spasi di dalamnya.
Jean-François Fabre

11

Untuk file besar, yang saya lakukan adalah membaca file baris demi baris dan membaca setiap baris menjadi sebuah array. Kemudian, setelah array mencapai ukuran tertentu, tambahkan ke file baru.

for line in open("file.txt", "r"):
    list.append(line)
    if len(list) == 1000000: 
        output.writelines(list)
        del list[:]

2
ini sepertinya sedikit berlebihan karena penulis harus menangani buffering. for l in open('file.txt','r'): output.write(l)harus bekerja temukan; hanya mengatur buffer aliran output sesuai kebutuhan Anda. atau Anda dapat pergi dengan byte dengan mengulangi coba dengan di output.write(read(n)); output.flush()mana njumlah byte yang ingin Anda tulis sekaligus. keduanya juga tidak memiliki syarat untuk memeriksa mana yang merupakan bonus.
Memiliki

1
Ya, tapi saya pikir mungkin ini bisa lebih mudah dimengerti karena menyalin seluruh baris daripada bagian-bagiannya (kalau-kalau kita tidak tahu berapa byte setiap baris).
ytpillai

Sangat benar. Pengkodean untuk pengajaran dan pengkodean untuk efisiensi sangat berbeda.
Memiliki

1
melihat sumbernya - panggilan writelines tulis, hg.python.org/cpython/file/c6880edaf6f3/Modules/_io/bytesio.c . Juga, aliran file sudah terbuka, jadi tulis tidak perlu membukanya lagi setiap waktu.
Memiliki

2
Ini mengerikan. Itu bekerja tidak perlu tanpa alasan yang bagus. Itu tidak berfungsi untuk file sewenang-wenang. Salinan tidak byte-identik jika input memiliki ujung garis yang tidak biasa pada sistem seperti Windows. Menurut Anda mengapa ini mungkin lebih mudah dipahami daripada panggilan ke fungsi salin shutil? Bahkan ketika mengabaikan shutil, loop blok baca / tulis sederhana (menggunakan IO unbuffered) langsung, akan efisien dan akan jauh lebih masuk akal daripada ini, dan dengan demikian tentu lebih mudah untuk diajarkan dan dipahami.
maxschlepzig

11
from subprocess import call
call("cp -p <file> <file>", shell=True)

10
Ini tergantung pada platform, jadi saya tidak akan menggunakannya.
Kevin Meier

5
Seperti callitu tidak aman. Silakan merujuk pada dokumen subproses tentang hal itu.
buhtz

2
ini tidak portabel, dan tidak perlu karena Anda bisa menggunakan shutil.
Corey Goldberg

2
Hmm mengapa Python?
Baris Demiray

Mungkin mendeteksi sistem operasi sebelum memulai (apakah itu DOS atau Unix, karena itu adalah dua yang paling sering digunakan)
MilkyWay90

8

Pada Python 3.5 Anda dapat melakukan hal berikut untuk file kecil (yaitu: file teks, jpeg kecil):

from pathlib import Path

source = Path('../path/to/my/file.txt')
destination = Path('../path/where/i/want/to/store/it.txt')
destination.write_bytes(source.read_bytes())

write_bytes akan menimpa apa pun yang ada di lokasi tujuan


2
Dan kemudian seseorang menggunakan kode (secara tidak sengaja atau sengaja) pada file besar ... Menggunakan fungsi dari shutilmenangani semua kasus khusus untuk Anda dan memberi Anda ketenangan pikiran.
Marcel Waldvogel

4
setidaknya itu tidak mengulangi solusi yang sama berulang-ulang.
Jean-François Fabre

6
open(destination, 'wb').write(open(source, 'rb').read())

Buka file sumber dalam mode baca, dan tulis ke file tujuan dalam mode tulis.


1
Idenya bagus dan kodenya indah, tetapi fungsi salin () yang tepat dapat melakukan lebih banyak hal, seperti menyalin atribut (+ x bit), atau misalnya menghapus byte yang sudah disalin dalam kasus kondisi disk-penuh ditemukan .
Raúl Salinas-Monteagudo

1
Semua jawaban perlu penjelasan, meskipun hanya satu kalimat. Tidak ada penjelasan yang menetapkan preseden buruk dan tidak membantu dalam memahami program. Bagaimana jika noob Python lengkap datang dan melihat ini, ingin menggunakannya, tetapi tidak bisa karena mereka tidak memahaminya? Anda ingin membantu semua dalam jawaban Anda.
connectyourcharger

1
Bukankah itu melewatkan .close()semua open(...)itu?
luckydonald

Tidak perlu .close (), karena kita TIDAK MENYIMPAN objek penunjuk file di mana saja (baik untuk file src maupun untuk file tujuan).
S471

1
Pendekatan pemborosan memori suboptimal yang sama dengan jawaban yellow01 .
maxschlepzig

-3

Python menyediakan fungsi bawaan untuk menyalin file dengan mudah menggunakan utilitas Shell Sistem Pengoperasian.

Perintah berikut digunakan untuk menyalin file

shutil.copy(src,dst)

Perintah berikut digunakan untuk menyalin file dengan informasi MetaData

shutil.copystat(src,dst)

Anda harus menjalankan copykemudian copystatuntuk melestarikan file metadata. Dalam Python 3.3+ copystatjuga menyalin atribut yang diperluas.
ingyhere
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.