Saya harus ke format std::string
dengan sprintf
dan mengirimkannya ke file streaming. Bagaimana saya bisa melakukan ini?
c++
string
formatting
stdstring
c++-standard-library
Max Frai
sumber
sumber
boost::format
(seperti solusi kennytm gunakan di sini ).boost::format
sudah mendukung operator stream C ++ juga! Contoh:cout << format("helloworld. a=%s, b=%s, c=%s") % 123 % 123.123 % "this is a test" << endl;
.boost::format
memiliki paling sedikit baris kode ... ditinjau oleh rekan dan terintegrasi dengan baik dengan aliran C ++.std::format
telah ditambahkan ke C ++ 20 BTW: stackoverflow.com/a/57286312/895245 Keren!C++20
baru kemarin dan saya melihat bahwaC++20
disalinboost
(untuk kesejuta kali sekarang) dengan menambahkanstd::format
keC++20
spesifikasi! Saya sangat sangat senang! Hampir setiap file C ++ yang saya tulis dalam 9 tahun terakhir telah digunakanboost::format
. menambahkan keluaran gaya printf resmi ke stream di C ++ akan sangat membantu IMO untuk semua C ++.Jawaban:
Anda tidak dapat melakukannya secara langsung, karena Anda tidak memiliki akses tulis ke buffer yang mendasarinya (sampai C ++ 11; lihat komentar Dietrich Epp ). Anda harus melakukannya terlebih dahulu dalam c-string, lalu salin ke std :: string:
Tapi saya tidak yakin mengapa Anda tidak hanya menggunakan aliran string? Saya berasumsi Anda memiliki alasan khusus untuk tidak hanya melakukan ini:
sumber
char buf[100];
membuat solusi ini tidak terlalu kuat. Tetapi ide penting ada di sana.asprintf
, yang mengalokasikan string baru dengan ruang yang cukup untuk menampung hasilnya. Kemudian salin kestd::string
jika Anda suka, dan ingat kefree
aslinya. Juga, dimungkinkan untuk meletakkan ini dalam makro sehingga setiap kompiler yang baik akan membantu memvalidasi format untuk Anda - Anda tidak ingin meletakkan didouble
mana a%s
diharapkanC ++ modern membuat ini sangat sederhana.
C ++ 20
C ++ 20 memperkenalkan
std::format
, yang memungkinkan Anda untuk melakukan hal itu. Ini menggunakan bidang pengganti yang mirip dengan yang ada di python :Lihat dokumentasi lengkapnya ! Ini merupakan peningkatan kualitas hidup yang luar biasa.
C ++ 11
Dengan C ++ 11 s
std::snprintf
, ini sudah menjadi tugas yang cukup mudah dan aman.Cuplikan kode di atas dilisensikan di bawah CC0 1.0 .
Penjelasan baris demi baris:
Tujuan: Menulis ke a
char*
dengan menggunakanstd::snprintf
lalu mengonversinya menjadi astd::string
.Pertama, kami menentukan panjang array char yang diinginkan menggunakan kondisi khusus di
snprintf
. Dari cppreference.com :Ini berarti bahwa ukuran yang diinginkan adalah jumlah karakter ditambah satu , sehingga null-terminator akan duduk setelah semua karakter lain dan dapat dipotong oleh konstruktor string lagi. Masalah ini dijelaskan oleh @ alexk7 di komentar.
snprintf
akan mengembalikan angka negatif jika terjadi kesalahan, jadi kami kemudian memeriksa apakah pemformatan berfungsi sesuai keinginan. Tidak melakukan ini dapat menyebabkan kesalahan diam atau alokasi buffer besar, seperti yang ditunjukkan oleh @ead di komentar.Selanjutnya, kami mengalokasikan array karakter baru dan menetapkannya ke a
std::unique_ptr
. Ini umumnya disarankan, karena Anda tidak perlu melakukannyadelete
lagi secara manual .Perhatikan bahwa ini bukan cara yang aman untuk mengalokasikan
unique_ptr
tipe yang ditentukan pengguna karena Anda tidak dapat membatalkan alokasi memori jika konstruktor melempar pengecualian!Setelah itu, kita tentu saja bisa menggunakan
snprintf
untuk tujuan penggunaannya dan menulis string yang diformat kechar[]
.Akhirnya, kami membuat dan mengembalikan yang baru
std::string
dari itu, memastikan untuk menghilangkan null-terminator di akhir.Anda dapat melihat contoh beraksi di sini .
Jika Anda juga ingin menggunakan
std::string
dalam daftar argumen, lihat intisari ini .Informasi tambahan untuk pengguna Visual Studio :
Sebagaimana dijelaskan dalam jawaban ini , Microsoft berganti nama
std::snprintf
menjadi_snprintf
(ya, tanpastd::
). MS selanjutnya menetapkannya sebagai usang dan menyarankan untuk menggunakannya_snprintf_s
sebagai gantinya, namun_snprintf_s
tidak akan menerima buffer menjadi nol atau lebih kecil dari output yang diformat dan tidak akan menghitung panjang output jika itu terjadi. Jadi untuk menghilangkan peringatan penghentian selama kompilasi, Anda dapat memasukkan baris berikut di bagian atas file yang berisi penggunaan_snprintf
:Pikiran terakhir
Banyak jawaban untuk pertanyaan ini ditulis sebelum waktu C ++ 11 dan menggunakan panjang buffer tetap atau vargs. Kecuali Anda terjebak dengan versi lama C ++, saya tidak akan merekomendasikan menggunakan solusi tersebut. Idealnya, gunakan cara C ++ 20.
Karena solusi C ++ 11 dalam jawaban ini menggunakan templat, ini dapat menghasilkan sedikit kode jika banyak digunakan. Namun, kecuali Anda mengembangkan untuk lingkungan dengan ruang biner yang sangat terbatas, ini tidak akan menjadi masalah dan masih merupakan peningkatan besar atas solusi lain dalam kejelasan dan keamanan.
Jika efisiensi ruang sangat penting, dua solusi dengan vargs dan vsnprintf ini dapat bermanfaat. JANGAN GUNAKAN solusi apa pun dengan panjang buffer tetap, yang hanya meminta masalah.
sumber
return string(&buf[0], size);
atau sesuatu yang serupa. Kedua, jika Anda mengembalikan c-string seperti itu, itu akan menyebabkan perilaku yang tidak terdefinisi karena vektor yang memegang nilai yang Anda tunjuk akan tidak valid saat dikembalikan. Ketiga, ketika saya mulai belajar C ++, standar tidak menentukan dalam elemen urutan apa yang harus disimpan di dalamstd::vector
, jadi mengakses penyimpanannya melalui pointer adalah perilaku yang tidak ditentukan. Sekarang ini akan berhasil, tetapi saya melihat tidak ada manfaatnya melakukannya dengan cara itu.std::string
akan dibangun dari vektor yang dikonversi secara implisit ( salin inisialisasi ), yang kemudian dikembalikan sebagai salinan, seperti yang disarankan oleh tanda tangan fungsi. Juga, unsur-unsur astd::vector
, dan selalu dimaksudkan untuk, disimpan secara bersebelahan . Tetapi saya berpendapat bahwa mungkin tidak ada manfaatnya untuk melakukannya.return string(buf.get(), buf.get() + size);
harusreturn string(buf.get(), buf.get() + size - 1);
lain Anda mendapatkan string dengan karakter nol di akhir. Saya menemukan ini menjadi kasus pada gcc 4.9.Solusi C ++ 11 yang menggunakan
vsnprintf()
internal:Pendekatan yang lebih aman dan efisien (saya mengujinya, dan lebih cepat):
Nilai
fmt_str
ini diteruskan untuk menyesuaikan dengan persyaratanva_start
.CATATAN: Versi "lebih aman" dan "lebih cepat" tidak berfungsi pada beberapa sistem. Karenanya keduanya masih terdaftar. Juga, "lebih cepat" tergantung sepenuhnya pada langkah preallokasi menjadi benar, jika tidak
strcpy
membuat itu lebih lambat.sumber
size
dalam setiap iterasi, ketika Anda bisa mendapatkannya dengan panggilan pertamavsnprintf()
.boost::format()
menyediakan fungsionalitas yang Anda inginkan:Seperti dari Boost format perpustakaan sinopsis:
sumber
C ++ 20 akan termasuk
std::format
yang menyerupaisprintf
dalam hal API tetapi sepenuhnya tipe-aman, bekerja dengan tipe yang ditentukan pengguna, dan menggunakan sintaks string format Python-like. Inilah cara Anda dapat memformatstd::string
dan menulisnya ke aliran:atau
Atau, Anda bisa menggunakan pustaka {fmt} untuk memformat string dan menulisnya ke
stdout
atau aliran file dalam sekali jalan:Adapun
sprintf
atau sebagian besar jawaban lain di sini, sayangnya mereka menggunakan varargs dan secara inheren tidak aman kecuali Anda menggunakan sesuatu sepertiformat
atribut GCC yang hanya berfungsi dengan string format literal. Anda dapat melihat mengapa fungsi-fungsi ini tidak aman pada contoh berikut:di mana
string_format
implementasi dari jawaban Erik Aronesty. Kode ini dikompilasi, tetapi kemungkinan besar akan macet ketika Anda mencoba menjalankannya:Penafian : Saya penulis {fmt} dan C ++ 20
std::format
.sumber
error: 'fmt' has not been declared
fmt
seperti pelaksanaan ditambahkan ke C ++ 20! stackoverflow.com/a/57286312/895245 fmt saat ini mengklaim dukungan untuk itu. Kerja luar biasa!Jika Anda hanya ingin sintaks seperti printf (tanpa memanggil printf sendiri), lihat Boost Format .
sumber
Saya menulis saya sendiri menggunakan vsnprintf sehingga mengembalikan string daripada harus membuat buffer saya sendiri.
Jadi Anda bisa menggunakannya seperti
sumber
vsnprintf
langsung ke dalam string.std::unique_ptr<char[]> buffer (new char[size]);
Untuk memformat
std::string
dengan cara 'sprintf', panggilsnprintf
(argumennullptr
dan0
) untuk mendapatkan panjang buffer yang diperlukan. Tulis fungsi Anda menggunakan template variadic C ++ 11 seperti ini:Kompilasi dengan dukungan C ++ 11, misalnya dalam GCC:
g++ -std=c++11
Pemakaian:
sumber
char buf[length + 1];
bukanchar* buf = new char[length + 1];
?char[]
danchar*
dengan yang baru, adalah bahwa dalam kasus sebelumnya buf akan dialokasikan pada stack. Tidak apa-apa untuk buffer kecil, tetapi karena kami tidak dapat menjamin ukuran string yang dihasilkan, itu sedikit lebih baik untuk digunakannew
. Sebagai contoh pada mesin sayastring_sprintf("value: %020000000d",5)
, cetak angka nol terkemuka sebelum angka 5, core dump ketika menggunakan array pada stack, tetapi berfungsi dengan baik ketika menggunakan array yang dialokasikan secara dinamisnew char[length + 1]
std::move
itu berlebihan .[edit: 20/05/25] lebih baik lagi ...:
Di tajuk:
The
PRINTSTRING(r)
-fungsi adalah untuk cater untuk GUI atau terminal atau output yang khusus kebutuhan menggunakan#ifdef _some_flag_
, defaultnya adalah:[edit '17 / 8/31] Menambahkan versi templated variadic 'vtspf (..)':
yang secara efektif adalah versi yang dibatasi koma (sebagai gantinya) dari
<<
-operator yang terkadang menghalangi , digunakan seperti ini:[Sunting] Diadaptasi untuk menggunakan teknik dalam jawaban Erik Aronesty (di atas):
[jawaban sebelumnya] Jawaban yang
sangat terlambat, tetapi bagi mereka yang, seperti saya, menyukai cara 'sprintf': Saya telah menulis dan menggunakan fungsi-fungsi berikut. Jika Anda suka, Anda dapat memperluas% -pilihan untuk lebih cocok dengan yang sprintf; yang ada di sana saat ini sudah cukup untuk kebutuhan saya. Anda menggunakan stringf () dan stringfappend () sama seperti Anda akan sprintf. Ingat saja bahwa parameter untuk ... harus tipe POD.
sumber
fmt
referensi itu berfungsi dengan baik. (Tapi saya kira orang-orang akan ingin bermain dengan mainan, jadi ...) Jika ada 'bug' lain yang bisa Anda jelaskan?Beginilah cara Google melakukannya:
StringPrintf
(Lisensi BSD)dan facebook melakukannya dengan cara yang sangat mirip:
StringPrintf
(Lisensi Apache)Keduanya memberikan kenyamanan
StringAppendF
juga.sumber
Dua sen saya untuk pertanyaan yang sangat populer ini.
Mengutip halaman manual
printf
fungsi-like :Dengan kata lain, implementasi C ++ 11 yang waras harus sebagai berikut:
Ini bekerja dengan sangat baik :)
Templat variadic hanya didukung di C ++ 11. Jawaban dari pixelpoint menunjukkan teknik serupa menggunakan gaya pemrograman yang lebih lama.
Sungguh aneh bahwa C ++ tidak memiliki hal seperti itu di luar kotak. Mereka baru-baru ini menambahkan
to_string()
, yang menurut saya merupakan langkah maju yang bagus. Saya ingin tahu apakah mereka akan menambahkan.format
operator kestd::string
...Edit
Seperti yang ditunjukkan alexk7, A
+1
diperlukan pada nilai pengembalianstd::snprintf
, karena kita perlu memiliki ruang untuk\0
byte. Secara intuitif, pada sebagian besar arsitektur yang hilang+1
akan menyebabkanrequired
bilangan bulat ditimpa sebagian dengan a0
. Ini akan terjadi setelah evaluasirequired
sebagai parameter aktual untukstd::snprintf
, sehingga efeknya tidak akan terlihat.Namun masalah ini dapat berubah, misalnya dengan optimisasi kompiler: bagaimana jika kompiler memutuskan untuk menggunakan register untuk
required
variabel? Ini adalah jenis kesalahan yang terkadang mengakibatkan masalah keamanan.sumber
bytes
buffer, mungkin di atasrequired
bilangan bulat (yang untungnya pada saat itu sudah dievaluasi).nullptr
argumen buffer, menghilangkanchar b;
baris dalam kode Anda. ( Sumber )Menggunakan C99 snprintf dan C ++ 11
sumber
Diuji, Jawaban Kualitas Produksi
Jawaban ini menangani kasus umum dengan teknik yang sesuai standar. Pendekatan yang sama diberikan sebagai contoh di CppReference.com di dekat bagian bawah halaman mereka. Tidak seperti contoh mereka, kode ini cocok dengan persyaratan pertanyaan dan diuji lapangan dalam aplikasi robotika dan satelit. Ini juga telah meningkatkan komentar. Kualitas desain dibahas lebih lanjut di bawah ini.
Efisiensi Linier yang Dapat Diprediksi
Dua lintasan adalah kebutuhan untuk fungsi yang dapat digunakan kembali yang aman, andal, dan dapat diprediksi sesuai spesifikasi pertanyaan. Anggapan tentang distribusi ukuran muatan dalam fungsi yang dapat digunakan kembali adalah gaya pemrograman yang buruk dan harus dihindari. Dalam hal ini, representasi panjang variabel yang besar dan sewenang-wenang merupakan faktor kunci dalam pemilihan algoritma.
Mencoba ulang pada overflow secara eksponensial tidak efisien, yang merupakan alasan lain yang dibahas ketika komite standar C ++ 11 membahas proposal di atas untuk memberikan proses yang kering ketika buffer tulis nol.
Dalam implementasi siap produksi di atas, proses pertama adalah proses kering untuk menentukan ukuran alokasi. Tidak ada alokasi yang terjadi. Mengurai arahan printf dan membaca Vargs telah dibuat sangat efisien selama beberapa dekade. Kode yang dapat digunakan kembali harus dapat diprediksi, meskipun inefisiensi kecil untuk kasus sepele harus dikorbankan.
Keamanan dan Keandalan
Andrew Koenig berkata kepada sekelompok kecil dari kami setelah ceramahnya di sebuah acara di Cambridge, "Fungsi pengguna tidak seharusnya bergantung pada eksploitasi kegagalan fungsi fungsional yang tidak biasa." Seperti biasa, kebijaksanaannya telah terbukti benar dalam catatan sejak itu. Masalah bug keamanan tetap dan tertutup sering menunjukkan retry hacks dalam deskripsi lubang yang dieksploitasi sebelum perbaikan.
Ini disebutkan dalam proposal revisi standar formal untuk fitur buffer nol di Alternatif ke sprintf, Proposal Revisi C9X , Dokumen ISO IEC WG14 N645 / X3J11 96-008 . String panjang sewenang-wenang yang dimasukkan per arahan cetak, "% s," dalam batasan ketersediaan memori dinamis, tidak terkecuali, dan tidak boleh dieksploitasi untuk menghasilkan, "Fungsionalitas luar biasa."
Pertimbangkan proposal di samping kode contoh yang diberikan di bagian bawah halaman C ++ Reference.org yang ditautkan pada paragraf pertama dari jawaban ini.
Juga, pengujian kasus kegagalan jarang sama kuatnya dengan kasus sukses.
Portabilitas
Semua vendor OS utama menyediakan kompiler yang sepenuhnya mendukung std :: vsnprintf sebagai bagian dari standar c ++ 11. Host yang menjalankan produk vendor yang tidak lagi memelihara distribusi harus dilengkapi dengan g ++ atau clang ++ karena berbagai alasan.
Penggunaan Stack
Penggunaan stack pada panggilan ke-1 ke std :: vsnprintf akan kurang dari atau sama dengan panggilan ke-2, dan akan dibebaskan sebelum panggilan ke-2 dimulai. Jika panggilan pertama melebihi ketersediaan tumpukan, maka std :: fprintf akan gagal juga.
sumber
C ++ 20
std::format
Itu telah datang! Fitur ini dijelaskan di: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0645r9.html dan menggunakan
.format()
sintaks mirip-Python .Saya berharap penggunaannya akan seperti:
Saya akan mencobanya ketika dukungan datang ke GCC, GCC 9.1.0 dengan
g++-9 -std=c++2a
masih tidak mendukungnya.API akan menambahkan
std::format
header baru :fmt
Pustaka yang ada mengklaim untuk mengimplementasikannya jika Anda memerlukan polyfill: https://github.com/fmtlib/fmtdan sebelumnya disebutkan di: std :: pemformatan string seperti sprintf
sumber
Berdasarkan jawaban yang diberikan oleh Erik Aronesty:
Ini menghindari kebutuhan untuk membuang
const
dari hasil.c_str()
yang ada dalam jawaban asli.sumber
sumber
_vscprintf
itu. Saya pikir Anda harus menguraikan jawaban ini.string tidak memiliki apa yang Anda butuhkan, tetapi std :: stringstream tidak. Gunakan stringstream untuk membuat string dan kemudian mengekstrak string. Berikut adalah daftar lengkap tentang hal-hal yang dapat Anda lakukan. Sebagai contoh:
akan memberi Anda 10 tempat desimal presisi saat mencetak double atau float.
sumber
Anda bisa mencoba ini:
sumber
Jika Anda berada di sistem yang memiliki asprintf (3) , Anda dapat dengan mudah membungkusnya:
sumber
format
, karena memberitahu gcc untuk memeriksa jenis argumen dan memberikan peringatan yang layak dengan -Dinding:std::string format(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
va_end
. "Jika va_end tidak dipanggil sebelum fungsi yang memanggil va_start atau va_copy kembali, perilaku tidak terdefinisi." - docsthrow std::bad_alloc();
, karena saya tidak menggunakan pengecualian C ++ di basis kode saya, dan untuk orang yang melakukannya, mereka dapat dengan mudah menambahkannya berdasarkan pada sumber komentar dan komentar Anda di sini.Ini adalah kode yang saya gunakan untuk melakukan ini di program saya ... Ini bukan sesuatu yang mewah, tetapi itu triknya ... Catatan, Anda harus menyesuaikan ukuran Anda sebagaimana berlaku. MAX_BUFFER bagi saya adalah 1024.
sumber
vsnprintf
langsung ke dalam string.Mengambil ide dari jawaban Dacav dan pixelpoint . Saya bermain-main sedikit dan mendapatkan ini:
Dengan praktik pemrograman waras saya percaya kode harus cukup, namun saya masih terbuka untuk alternatif yang lebih aman yang masih cukup sederhana dan tidak memerlukan C ++ 11.
Dan inilah versi lain yang menggunakan buffer awal untuk mencegah panggilan kedua
vsnprintf()
ketika buffer awal sudah cukup.(Ternyata versi ini mirip dengan jawaban Piti Ongmongkolkul , hanya saja tidak digunakan
new
dandelete[]
, dan juga menentukan ukuran saat membuatstd::string
.Gagasan di sini untuk tidak menggunakan
new
dandelete[]
menyiratkan penggunaan tumpukan di atas tumpukan karena itu tidak perlu memanggil fungsi alokasi dan deallokasi, namun jika tidak digunakan dengan benar, bisa berbahaya untuk buffer overflow di beberapa (mungkin lama, atau mungkin hanya sistem yang rentan). Jika ini merupakan masalah, saya sangat menyarankan menggunakannew
dandelete[]
sebagai gantinya. Perhatikan bahwa satu-satunya masalah di sini adalah tentang alokasi sepertivsnprintf()
yang sudah disebut dengan batas, jadi menentukan batas berdasarkan ukuran yang dialokasikan pada buffer kedua juga akan mencegahnya.)sumber
Saya biasanya menggunakan ini:
Kerugian: tidak semua sistem mendukung vasprint
sumber
Versi @iFreilicht jawaban yang sedikit dimodifikasi, diperbarui ke C ++ 14 (penggunaan
make_unique
fungsi alih-alih deklarasi mentah) dan menambahkan dukungan untukstd::string
argumen (berdasarkan artikel Kenny Kerr )Keluaran:
Silakan menggabungkan jawaban ini dengan yang asli jika diinginkan.
sumber
Pustaka Poco Foundation memiliki fungsi format yang sangat nyaman, yang mendukung std :: string baik dalam format string maupun nilainya:
sumber
Anda dapat memformat output C ++ dalam cout menggunakan file header iomanip. Pastikan Anda menyertakan file header iomanip sebelum Anda menggunakan salah satu fungsi pembantu seperti setprecision, setfill dll.
Berikut ini cuplikan kode yang saya gunakan di masa lalu untuk mencetak waktu tunggu rata-rata dalam vektor, yang telah saya "kumpulkan".
Berikut ini adalah deskripsi singkat tentang bagaimana kami dapat memformat aliran C ++. http://www.cprogramming.com/tutorial/iomanip.html
sumber
Mungkin ada masalah, jika buffer tidak cukup besar untuk mencetak string. Anda harus menentukan panjang string yang diformat sebelum mencetak pesan yang diformat di sana. Saya membuat pembantu sendiri untuk ini (diuji pada Windows dan Linux GCC ), dan Anda dapat mencoba menggunakannya.
String.cpp: http://pastebin.com/DnfvzyKP
String.h: http://pastebin.com/7U6iCUMa
String.cpp:
String.h:
sumber
vsnprintf((char *)dst.data(), dst.size() + 1, format, ap);
- Apakah aman untuk mengasumsikan buffer string memiliki ruang untuk mengakhiri karakter null? Apakah ada implementasi yang tidak mengalokasikan ukuran + 1 karakter. Akankah lebih aman untuk melakukannyadst.resize(length+1); vsnprintf((char *)dst.data(), dst.size(), format, ap); dst.resize(length);
data
danc_str
sinonim.sumber
Solusi yang sangat-sangat sederhana.
sumber
Saya menyadari ini telah dijawab berkali-kali, tetapi ini lebih ringkas:
contoh:
Lihat juga http://rextester.com/NJB14150
sumber
UPDATE 1 :
fmt::format
tes ditambahkanSaya telah mengambil investigasi saya sendiri di sekitar metode yang telah diperkenalkan di sini dan mendapatkan hasil yang berlawanan secara berlawanan dengan yang disebutkan di sini.
Saya telah menggunakan 4 fungsi lebih dari 4 metode:
vsnprintf
+std::unique_ptr
vsnprintf
+std::string
std::ostringstream
+std::tuple
+utility::for_each
fmt::format
fungsi darifmt
perpustakaanUntuk tes backend yang
googletest
telah digunakan.The
for_each
pelaksanaan diambil dari sini: iterate lebih tupleTes:
The
UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR
.unsued.hpp :
unused.cpp :
HASIL :
Seperti yang Anda lihat implementasi melalui
vsnprintf
+std::string
sama denganfmt::format
, tetapi lebih cepat daripada melaluivsnprintf
+std::unique_ptr
, yang lebih cepat daripada melaluistd::ostringstream
.Tes dikompilasi
Visual Studio 2015 Update 3
dan dijalankan padaWindows 7 x64 / Intel Core i7-4820K CPU @ 3.70GHz / 16GB
.sumber