C ++ Chuyển đổi chuỗi (hoặc char *) thành chuỗi (hoặc wchar_t *)


171
string s = "おはよう";
wstring ws = FUNCTION(s, ws);

Làm thế nào tôi có thể gán nội dung của s cho ws?

Tìm kiếm trên google và sử dụng một số kỹ thuật nhưng họ không thể chỉ định nội dung chính xác. Nội dung bị bóp méo.


7
Tôi không nghĩ stringschấp nhận các ký tự 8 bit. Có phải nó đã được mã hóa trong UTF-8?
kennytm

3
Hệ thống mã hóa của bạn là gì mà nó sẽ tạo ra "おはよう"một chuỗi mã hóa hệ thống?
sbi

Tôi tin rằng MSVC sẽ chấp nhận điều đó và biến nó thành một số mã hóa đa bào, có thể là UTF-8.
Potatoswatter

1
@Potatoswatter: MSVC không sử dụng UTF-8 theo mặc định cho MỌI THỨ. Nếu bạn nhập các ký tự đó, nó sẽ yêu cầu mã hóa để chuyển đổi tệp thành và mặc định thành codepage 1252.
Vịt Mooing

2
@Samir: quan trọng hơn là mã hóa của tập tin là gì? Bạn có thể di chuyển chuỗi đó đến phần đầu của tập tin và hiển thị một hexdump của phần đó không? Chúng ta có thể xác định nó từ đó.
Vịt Mooing

Câu trả lời:


239

Giả sử rằng chuỗi đầu vào trong ví dụ của bạn (は) là mã UTF-8 (mà không phải, bởi vẻ bề ngoài của nó, nhưng giả sử nó là vì sự giải thích này :-)) đại diện cho chuỗi Unicode bạn quan tâm, sau đó vấn đề của bạn có thể được giải quyết hoàn toàn chỉ với thư viện chuẩn (C ++ 11 trở lên).

Phiên bản TL; DR:

#include <locale>
#include <codecvt>
#include <string>

std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
std::string narrow = converter.to_bytes(wide_utf16_source_string);
std::wstring wide = converter.from_bytes(narrow_utf8_source_string);

Ví dụ trực tuyến có thể biên dịch và chạy được lâu hơn:

(Tất cả đều hiển thị cùng một ví dụ. Chỉ có nhiều thứ để dự phòng ...)

Lưu ý (cũ) :

Như đã chỉ ra trong các nhận xét và được giải thích trong https://stackoverflow.com/a/17106065/6345, có những trường hợp khi sử dụng thư viện chuẩn để chuyển đổi giữa UTF-8 và UTF-16 có thể mang lại sự khác biệt bất ngờ về kết quả trên các nền tảng khác nhau . Để chuyển đổi tốt hơn, hãy xem xét std::codecvt_utf8như được mô tả trên http://en.cppreference.com/w/cpp/locale/codecvt_utf8

Lưu ý (mới) :

codecvttiêu đề không được dùng trong C ++ 17, một số lo lắng về giải pháp được trình bày trong câu trả lời này đã được nêu ra. Tuy nhiên, ủy ban tiêu chuẩn C ++ đã thêm một tuyên bố quan trọng trong http://www.open-std.org/jtc1/sc22/wg21/docs/ con / 2017 / p0618r0.html nói

thành phần thư viện này nên được rút về Phụ lục D, dọc theo, cho đến khi một sự thay thế phù hợp được tiêu chuẩn hóa.

Vì vậy, trong tương lai gần, codecvtgiải pháp trong câu trả lời này là an toàn và di động.


2
Kiểm tra mã hóa nào bạn lưu tệp VS với
Johann Gerell

9
Hãy lưu ý rằng đây chỉ là C ++ 11!
bk138

1
Trong minGW (gcc / g ++ 4.8.1 và -std = c ++ 11) tiêu đề codecvt không tồn tại. Có một sự thay thế?
Brian Jack

1
Bạn có thể vui lòng cung cấp một ví dụ về std::codecvt_utf8người mới bắt đầu
Noitidart

14
Xin lưu ý rằng <codecvt>không được chấp nhận kể từ C ++ 17.
tambre

47
int StringToWString(std::wstring &ws, const std::string &s)
{
    std::wstring wsTmp(s.begin(), s.end());

    ws = wsTmp;

    return 0;
}

93
Điều này chỉ hoạt động nếu tất cả các ký tự là byte đơn, tức là ASCII hoặc ISO-8859-1 . Bất cứ điều gì nhiều byte sẽ thất bại thảm hại, bao gồm UTF-8. Câu hỏi rõ ràng chứa các ký tự nhiều byte.
Đánh dấu tiền chuộc

28
Câu trả lời này rõ ràng là không đủ và không làm gì ngoài việc sao chép các ký tự hẹp như thành các ký tự rộng. Xem các câu trả lời khác, đặc biệt là câu trả lời của Johann Gerell, để biết cách đi đúng từ một chuỗi được mã hóa nhiều byte hoặc utf8 sang một chuỗi utf16.
DLRdave

10
Câu trả lời này là nguy hiểm và có thể sẽ phá vỡ trên hệ thống không phải ascii. tức là một tên tệp tiếng Ả Rập sẽ bị xáo trộn bởi vụ hack này.
Stephen

9
Câu trả lời này rất hữu ích nếu bạn bỏ qua sắc thái của câu hỏi và tập trung vào tiêu đề câu hỏi, đó là điều đã đưa tôi đến đây từ Google. Như vậy, tiêu đề của câu hỏi cực kỳ sai lệch và nên được thay đổi để phản ánh câu hỏi thực sự đang được hỏi
Anne Quinn

3
Điều này chỉ hoạt động cho các ký tự ASCII 7 bit. Đối với latin1, nó chỉ hoạt động nếu char được cấu hình là không dấu. Nếu loại char được ký (hầu hết thời gian là như vậy), các ký tự> 127 sẽ cho kết quả sai.
huyc

32

Câu hỏi của bạn không được xác định rõ. Nghiêm túc, ví dụ đó là một lỗi cú pháp. Tuy nhiên, std::mbstowcscó lẽ là những gì bạn đang tìm kiếm.

Đây là một chức năng của thư viện C và hoạt động trên các bộ đệm, nhưng đây là một thành ngữ dễ sử dụng, lịch sự của TBohne (trước đây là Mooing Duck):

std::wstring ws(s.size(), L' '); // Overestimate number of code points.
ws.resize(std::mbstowcs(&ws[0], s.c_str(), s.size())); // Shrink to fit.

1
chuỗi s = "お よ"; wchar_t * buf = new wchar_t [s.size ()]; size_t num_chars = mbstowcs (buf, s.c_str (), s.size ()); chuỗi ws (buf, num_chars); // ws = bị bóp méo
Samir

1
@Samir: Bạn phải đảm bảo mã hóa thời gian chạy giống như mã hóa thời gian biên dịch. Bạn có thể cần setlocalehoặc điều chỉnh cờ trình biên dịch. Tôi không biết vì tôi không sử dụng Windows, nhưng đây là lý do tại sao nó không phải là một tính năng phổ biến. Hãy xem xét câu trả lời khác nếu có thể.
Potatoswatter

1
std::string ws(s.size()); ws.resize(mbstowcs(&ws[0], s.c_str(), s.size());RAII FTW
Vịt Mooing

2
@WaffSouffle Điều đó đã lỗi thời. Việc triển khai tiếp giáp đã được yêu cầu từ năm 2011 và việc triển khai đã bỏ các thủ thuật như vậy từ lâu trước đó.
Potatoswatter

1
và một số môi trường như mingw vẫn không có tiêu đề codecvt nên một số giải pháp 'tốt hơn' trước đó không hoạt động có nghĩa là vấn đề này vẫn không có giải pháp tốt trong mingw ngay cả vào tháng 12 năm 2014
Brian Jack

18

Chỉ API Windows, triển khai trước C ++ 11, trong trường hợp ai đó cần nó:

#include <stdexcept>
#include <vector>
#include <windows.h>

using std::runtime_error;
using std::string;
using std::vector;
using std::wstring;

wstring utf8toUtf16(const string & str)
{
   if (str.empty())
      return wstring();

   size_t charsNeeded = ::MultiByteToWideChar(CP_UTF8, 0, 
      str.data(), (int)str.size(), NULL, 0);
   if (charsNeeded == 0)
      throw runtime_error("Failed converting UTF-8 string to UTF-16");

   vector<wchar_t> buffer(charsNeeded);
   int charsConverted = ::MultiByteToWideChar(CP_UTF8, 0, 
      str.data(), (int)str.size(), &buffer[0], buffer.size());
   if (charsConverted == 0)
      throw runtime_error("Failed converting UTF-8 string to UTF-16");

   return wstring(&buffer[0], charsConverted);
}

Bạn có thể tối ưu hóa nó. Không cần phải sao chép chuỗi bằng cách sử dụng a vector. Chỉ cần dự trữ các ký tự trong chuỗi bằng cách thực hiện wstring strW(charsNeeded + 1);và sau đó sử dụng nó làm bộ đệm để chuyển đổi : &strW[0]. Cuối cùng đảm bảo null cuối cùng có mặt sau khi chuyển đổi bằng cách thực hiệnstrW[charsNeeded] = 0;
c00000fd

1
@ c00000fd, theo như tôi biết, bộ đệm nội bộ std :: basic_ chuỗi được yêu cầu phải liên tục chỉ kể từ tiêu chuẩn C ++ 11. Mã của tôi là tiền C ++ 11, như được ghi chú trên đầu bài. Do đó, mã & strW [0] sẽ không tuân thủ tiêu chuẩn và có thể bị sập hợp pháp khi chạy.
Alex Che

13

Nếu bạn đang sử dụng Windows / Visual Studio và cần chuyển đổi một chuỗi thành chuỗi, bạn có thể sử dụng:

#include <AtlBase.h>
#include <atlconv.h>
...
string s = "some string";
CA2W ca2w(s.c_str());
wstring w = ca2w;
printf("%s = %ls", s.c_str(), w.c_str());

Quy trình tương tự để chuyển đổi một chuỗi thành chuỗi (đôi khi bạn sẽ cần chỉ định một bảng mã ):

#include <AtlBase.h>
#include <atlconv.h>
...
wstring w = L"some wstring";
CW2A cw2a(w.c_str());
string s = cw2a;
printf("%s = %ls", s.c_str(), w.c_str());

Bạn có thể chỉ định một bảng mã và thậm chí UTF8 (điều đó khá hay khi làm việc với JNI / Java ). Một cách tiêu chuẩn để chuyển đổi chuỗi std :: w chuỗi thành utf8 std :: chuỗi được hiển thị trong câu trả lời này .

// 
// using ATL
CA2W ca2w(str, CP_UTF8);

// 
// or the standard way taken from the answer above
#include <codecvt>
#include <string>

// convert UTF-8 string to wstring
std::wstring utf8_to_wstring (const std::string& str) {
    std::wstring_convert<std::codecvt_utf8<wchar_t>> myconv;
    return myconv.from_bytes(str);
}

// convert wstring to UTF-8 string
std::string wstring_to_utf8 (const std::wstring& str) {
    std::wstring_convert<std::codecvt_utf8<wchar_t>> myconv;
    return myconv.to_bytes(str);
}

Nếu bạn muốn biết thêm về tiền mã hóa, có một bài viết thú vị về Joel trên Phần mềm: Tối thiểu tuyệt đối Mỗi nhà phát triển phần mềm Tuyệt đối, Phải tích cực phải biết về Unicode và Bộ ký tự .

Các macro CA2W (Chuyển đổi Ansi thành Wide = unicode) là một phần của Macro chuyển đổi chuỗi ATL và MFC , bao gồm các mẫu.

Đôi khi bạn sẽ cần phải tắt cảnh báo bảo mật # 4995 ', tôi không biết cách giải quyết khác (với tôi nó xảy ra khi tôi biên dịch cho WindowsXp trong VS2012).

#pragma warning(push)
#pragma warning(disable: 4995)
#include <AtlBase.h>
#include <atlconv.h>
#pragma warning(pop)

Chỉnh sửa: Vâng, theo bài viết này , bài viết của Joel dường như là: "trong khi giải trí, nó khá nhẹ về các chi tiết kỹ thuật thực tế". Bài báo: Điều mà mọi lập trình viên hoàn toàn cần, tích cực cần biết về mã hóa và bộ ký tự để làm việc với văn bản .


Xin lỗi tôi không phải là người nói tiếng Anh bản ngữ. Vui lòng chỉnh sửa khi bạn thấy phù hợp.
lmiguelmh

Có chuyện gì với downvoter? Có gì sai với câu trả lời?
lmiguelmh

Có lẽ thực tế là nó thúc đẩy mã không di động.
Pavel Minaev

Vâng, đó là lý do tại sao tôi tuyên bố rằng điều này chỉ hoạt động trong Windows / Visual Studio. Nhưng ít nhất giải pháp này là chính xác, và không phải giải pháp này:char* str = "hello worlddd"; wstring wstr (str, str+strlen(str));
lmiguelmh

Lưu ý thêm: CA2W nằm trong không gian tên của ATL. (ATL :: CA2W)
Val

12

Dưới đây là một cách để kết hợp string, wstringvà chuỗi hằng hỗn hợp để wstring. Sử dụng wstringstreamlớp học.

Điều này KHÔNG hoạt động đối với mã hóa ký tự nhiều byte. Đây chỉ là một cách ngu ngốc để loại bỏ sự an toàn của loại và mở rộng các ký tự 7 bit từ std :: chuỗi thành 7 bit thấp hơn của mỗi ký tự của std: wopes. Điều này chỉ hữu ích nếu bạn có chuỗi ASCII 7 bit và bạn cần gọi API yêu cầu chuỗi rộng.

#include <sstream>

std::string narrow = "narrow";
std::wstring wide = L"wide";

std::wstringstream cls;
cls << " abc " << narrow.c_str() << L" def " << wide.c_str();
std::wstring total= cls.str();

Câu trả lời có vẻ thú vị. Bạn có thể vui lòng giải thích một chút: điều này sẽ hoạt động cho các bảng mã nhiều byte, và tại sao / làm thế nào?
wh1t3cat1k

sơ đồ mã hóa là trực giao với lớp lưu trữ. stringlưu trữ 1 ký tự byte và wstringlưu trữ 2 ký tự byte. một cái gì đó như utf8 lưu trữ các ký tự mulitbyte dưới dạng một chuỗi các giá trị 1 byte, tức là trong a string. các lớp chuỗi không giúp mã hóa. Tôi không phải là một chuyên gia về các lớp mã hóa trong c ++.
Đánh dấu Lakata

2
Bất kỳ lý do tại sao câu hỏi này không phải là câu trả lời tốt nhất, vì nó ngắn và đơn giản như thế nào? Bất kỳ trường hợp nào nó không bao gồm?
Ryuu

@MarkLakata, tôi đã đọc câu trả lời của bạn cho bình luận đầu tiên nhưng tôi vẫn không chắc chắn. Nó sẽ làm việc cho các ký tự nhiều byte? Nói cách khác, nó không dễ bị cạm bẫy như câu trả lời này ?
Marc.2377

@ Marc.2377 Điều này KHÔNG hoạt động đối với mã hóa ký tự nhiều byte. Đây chỉ là một cách ngu ngốc để loại bỏ sự an toàn của loại và mở rộng các ký tự 7 bit từ std::string7 bit thấp hơn của mỗi ký tự std:wstring. Điều này chỉ hữu ích nếu bạn có chuỗi ASCII 7 bit và bạn cần gọi API yêu cầu chuỗi rộng. Nhìn vào stackoverflow.com/a/8969776/3258851 nếu bạn cần một cái gì đó tinh vi hơn.
Đánh dấu Lakata

11

Từ char*đến wstring:

char* str = "hello worlddd";
wstring wstr (str, str+strlen(str));

Từ stringđến wstring:

string str = "hello worlddd";
wstring wstr (str.begin(), str.end());

Lưu ý điều này chỉ hoạt động tốt nếu chuỗi được chuyển đổi chỉ chứa các ký tự ASCII.


7
Bởi vì điều này chỉ hoạt động nếu mã hóa là Windows-1252, thậm chí không thể giữ các chữ cái trong câu hỏi.
Vịt Mooing

3
Đây là cách dễ bị lỗi nhất, khi bạn biết bạn đang xử lý ASCII. Đó là một usecase nổi bật khi chuyển các ứng dụng sang api mới hơn.
Sid Sarasvati

Đây không phải là cách. Nếu bạn đang sử dụng Visual Studio, bạn nên sử dụng atlconv.h. Kiểm tra các câu trả lời khác.
lmiguelmh

7

sử dụng Boost.Locale:

ws = boost::locale::conv::utf_to_utf<wchar_t>(s);

5

Biến thể này của nó là yêu thích của tôi trong cuộc sống thực. Nó chuyển đổi đầu vào, nếu nó là UTF-8 hợp lệ , thành tương ứng wstring. Nếu đầu vào bị hỏng, thì wstringđược xây dựng từ các byte đơn. Điều này cực kỳ hữu ích nếu bạn không thể thực sự chắc chắn về chất lượng dữ liệu đầu vào của mình.

std::wstring convert(const std::string& input)
{
    try
    {
        std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
        return converter.from_bytes(input);
    }
    catch(std::range_error& e)
    {
        size_t length = input.length();
        std::wstring result;
        result.reserve(length);
        for(size_t i = 0; i < length; i++)
        {
            result.push_back(input[i] & 0xFF);
        }
        return result;
    }
}

1
Tôi vừa đưa ra câu hỏi này dựa trên câu trả lời stackoverflow.com/questions/49669048/ của bạn có thể vui lòng xem qua
MistyD

2

Nếu bạn có QT và nếu bạn lười thực hiện một chức năng và công cụ bạn có thể sử dụng

std :: chuỗi str; QString (str) .toStdWString ()


Hầu như, nhưng bạn chỉ nên bắt đầu với một QString, bởi vì hàm QStringtạo không thể chấp nhận một chuỗi vì một số lý do.
bobsbeenjamin


Cái này đẹp đấy. Ngoài ra, bạn có thể sử dụng .c_str () để cho QString chấp nhận chuỗi của bạn trong hàm tạo.
miep

1

phương pháp s2ws hoạt động tốt. Hy vọng giúp.

std::wstring s2ws(const std::string& s) {
    std::string curLocale = setlocale(LC_ALL, ""); 
    const char* _Source = s.c_str();
    size_t _Dsize = mbstowcs(NULL, _Source, 0) + 1;
    wchar_t *_Dest = new wchar_t[_Dsize];
    wmemset(_Dest, 0, _Dsize);
    mbstowcs(_Dest,_Source,_Dsize);
    std::wstring result = _Dest;
    delete []_Dest;
    setlocale(LC_ALL, curLocale.c_str());
    return result;
}

6
Điều gì với tất cả các câu trả lời này phân bổ bộ nhớ động theo cách không an toàn, và sau đó sao chép dữ liệu từ bộ đệm vào chuỗi? Tại sao không ai thoát khỏi người trung gian không an toàn?
Vịt Mooing

hahakubile, bạn có thể giúp tôi với một cái gì đó tương tự cho ws2s không?
cristian

1

Dựa trên thử nghiệm của riêng tôi (Trên windows 8, vs2010) mbstowcs thực sự có thể làm hỏng chuỗi gốc, nó chỉ hoạt động với trang mã ANSI. Nếu MultiByteToWideChar / WideCharToMultiByte cũng có thể gây ra tham nhũng chuỗi - nhưng họ có xu hướng thay thế các ký tự mà họ không biết bằng '?' dấu hỏi, nhưng mbstowcs có xu hướng dừng lại khi nó gặp ký tự không xác định và cắt chuỗi tại chính điểm đó. (Tôi đã thử các ký tự tiếng Việt trên các cửa sổ Phần Lan).

Vì vậy, thích chức năng api đa * hơn so với các chức năng tương tự ansi C.

Ngoài ra, điều mà tôi nhận thấy cách ngắn nhất để mã hóa chuỗi từ mã này sang mã khác là không sử dụng các hàm gọi api MultiByteToWideChar / WideCharToMultiByte nhưng các macro ATL tương tự của chúng: W2A / A2W.

Vì vậy, chức năng tương tự như đã đề cập ở trên sẽ nghe như:

wstring utf8toUtf16(const string & str)
{
   USES_CONVERSION;
   _acp = CP_UTF8;
   return A2W( str.c_str() );
}

_acp được khai báo trong macro USES_CONVERSION.

Hoặc cũng là chức năng mà tôi thường bỏ lỡ khi thực hiện chuyển đổi dữ liệu cũ sang dữ liệu mới:

string ansi2utf8( const string& s )
{
   USES_CONVERSION;
   _acp = CP_ACP;
   wchar_t* pw = A2W( s.c_str() );

   _acp = CP_UTF8;
   return W2A( pw );
}

Nhưng xin lưu ý rằng các macro đó sử dụng nhiều stack - không sử dụng cho các vòng lặp hoặc các vòng lặp đệ quy cho cùng một chức năng - sau khi sử dụng macro W2A hoặc A2W - tốt hơn để trả về ASAP, vì vậy stack sẽ được giải phóng khỏi chuyển đổi tạm thời.


1

Chuỗi thành chuỗi

std::wstring Str2Wstr(const std::string& str)
{
    int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
    std::wstring wstrTo(size_needed, 0);
    MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed);
    return wstrTo;
}

chuỗi đến chuỗi

std::string Wstr2Str(const std::wstring& wstr)
{
    typedef std::codecvt_utf8<wchar_t> convert_typeX;
    std::wstring_convert<convert_typeX, wchar_t> converterX;
    return converterX.to_bytes(wstr);
}

1
Str2Wstr này có vấn đề với 0 chấm dứt. Không thể nối các wstrings được tạo nữa thông qua "+" (như trong chuỗi s3 = s1 + s2). Tôi sẽ đăng một câu trả lời sớm giải quyết vấn đề này. Trước tiên phải làm một số thử nghiệm cho rò rỉ bộ nhớ.
thewhiteambit

-2

string s = "おはよう"; là một lỗi.

Bạn nên sử dụng trực tiếp chuỗi:

wstring ws = L"おはよう";

1
Điều đó cũng không đi làm. Bạn sẽ phải chuyển đổi các ký tự không phải BMP đó thành các chuỗi thoát C.
Dave Van den Eynde

3
@Dave: nó hoạt động nếu trình biên dịch của bạn hỗ trợ unicode trong các tệp nguồn và tất cả những cái trong thập kỷ trước đều làm (visual studio, gcc, ...)
Thomas Bonini

Xin chào, bất kể mã hóa hệ thống mặc định (ví dụ tôi có thể có mã Ả Rập làm mã hóa hệ thống mặc định), mã hóa của tệp mã nguồn để L "" hoạt động như thế nào? nó phải ở dạng UTF-16 hay tôi có thể có UTF-8 mà không có BOM để mã hóa tệp .cpp không?
Afriza N. Arief

2
@afriza: nó không thực sự quan trọng miễn là trình biên dịch của bạn hỗ trợ nó
Thomas Bonini

2
Nó không phải là một lỗi; các ký tự mở rộng trong một chuỗi "hẹp" được xác định để ánh xạ tới các chuỗi đa bào. Trình biên dịch sẽ hỗ trợ nó miễn là HĐH thực hiện, đây là điều tối thiểu bạn có thể yêu cầu.
Potatoswatter

-2

sử dụng mã này để chuyển đổi chuỗi của bạn thành chuỗi

std::wstring string2wString(const std::string& s){
    int len;
    int slength = (int)s.length() + 1;
    len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0); 
    wchar_t* buf = new wchar_t[len];
    MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len);
    std::wstring r(buf);
    delete[] buf;
    return r;
}

int main(){
    std::wstring str="your string";
    std::wstring wStr=string2wString(str);
    return 0;
}

3
Lưu ý rằng câu hỏi không đề cập đến Windows và câu trả lời này chỉ dành cho Windows.
Johann Gerell 27/8/2015

CP_ACPchắc chắn là lập luận sai. Thật bất ngờ, trạng thái môi trường của luồng thực thi có ảnh hưởng đến hành vi của mã. Không nên. Chỉ định mã hóa ký tự cố định trong chuyển đổi của bạn. (Và xem xét xử lý lỗi.)
IInspectable
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.