2009-11-02 13 views
7

निम्न कोड के UTF-8 उत्पादन मेरी मशीन पर अनपेक्षित व्यवहार (विंडोज 7 पर Windows XP और वी.एस. 2012 को दृश्य C++ 2008 SP1 के साथ परीक्षण किया गया) दिखाता है।Windows कंसोल

Windows XP: एक कंसोल विंडो में आउटपुट ü0ü (कोडपेज 1252 में अनुवाद, मूल रूप से डिफ़ॉल्ट कोडपेज, शायद 437 में charachters ड्राइंग कुछ लाइन से पता चलता है)। जब मैं सेटिंग में बदलाव कंसोल विंडो "ल्युसिडा कंसोल" वर्ण सेट का उपयोग करें और मेरी test.exe फिर से चलाने के लिए की , उत्पादन करने के लिए बदल जाता है, जो

  • चरित्र üfputs का उपयोग कर लिखा जा सकता है इसका मतलब है और उसके UTF-8 एन्कोडिंग C3 BC
  • std::cout जो भी कारण
  • धाराओं failbit चरित्र
लिखने की कोशिश कर के बाद स्थापित कर रही है के लिए काम नहीं करता है

विंडोज 7: कंसोलस का उपयोग कर आउटपुट ��0ü है। और भी दिलचस्प। सही बाइट्स लिखे गए हैं, संभवतः (कम से कम जब आउटपुट को फ़ाइल में रीडायरेक्ट करते हैं) और स्ट्रीम स्टेटस ठीक है, लेकिन दो बाइट अलग वर्णों के रूप में लिखे गए हैं)।

मैंने इस समस्या को "माइक्रोसॉफ्ट कनेक्ट" पर बढ़ाने की कोशिश की (here देखें), लेकिन एमएस बहुत उपयोगी नहीं रहा है। आप here देख सकते हैं जैसा कि पहले कुछ पूछा गया था।

क्या आप इस समस्या को पुन: उत्पन्न कर सकते हैं?

मैं क्या गलत कर रहा हूं? std::cout और fputs पर प्रभाव नहीं होना चाहिए?

हल: (एक तरह से) mike.dld के विचार के बाद मैं एक std::stringbuf कार्यान्वित sync() में विंडोज़ -1252 के लिए UTF-8 से रूपांतरण कर रहे हैं और (माइक पर मेरी टिप्पणी को देखने के लिए इस कनवर्टर के साथ std::cout की streambuf बदल दिया। डीएलडी का जवाब)।

+0

मुझे पहले C++ iostreams में परेशानी हुई है। वहां बहुत सारी छिपी हुई नीचता है जो समस्याओं का कारण बनती है। यह एक उत्तर के लायक नहीं है, लेकिन जब iostreams आपको परेशानी देता है, सी के stdio का उपयोग करें, मुझे इस तरह के मुद्दों के साथ कई बार पहले किया था। –

+0

हां, iostreams का उपयोग stdio से अधिक जटिल है, यहां तक ​​कि [पूर्ण लंबाई वाली पाठ्य पुस्तकों] भी हैं (http://www.amazon.com/Standard-Iostreams-Locales-Programmers-Reference/dp/0201183951) इसके बारे में। लेकिन iostreams आपको लचीलापन का एक बड़ा सौदा देता है, जिसे मैं खुशी से उपयोग कर रहा हूं। – mkluwe

+0

क्या यह विंडोज कंसोल की समस्या नहीं है? मुझे याद है कि यह किसी भी तरह से असीमित नहीं है, ऐसी कई समस्याएं पैदा कर रहा है ... –

उत्तर

0

अब इसे बंद करने का समय है। स्टीफन टी। Lavavej says व्यवहार "डिजाइन द्वारा" है, हालांकि मैं इस स्पष्टीकरण का पालन नहीं कर सकता।

मेरा वर्तमान ज्ञान है: यूटीएफ -8 कोडपेज में विंडोज एक्सपी कंसोल सी ++ iostreams के साथ काम नहीं करता है।

विंडोज एक्सपी अब फैशन से बाहर हो रहा है और वीएस 2008 भी करता है। मुझे यह जानने में दिलचस्पी होगी कि समस्या अभी भी नई विंडोज सिस्टम पर मौजूद है या नहीं।

विंडोज 7 पर प्रभाव शायद सी ++ स्ट्रीम आउटपुट वर्णों के तरीके के कारण होता है। जैसा कि Properly print utf8 characters in windows console के उत्तर में देखा गया है, यूटीएफ -8 आउटपुट सी स्टडीओ के साथ विफल रहता है जब एक बाइट को putc('\xc3'); putc('\xbc'); जैसे किसी अन्य के बाद प्रिंट किया जाता है। शायद यही सी ++ धाराएं यहां करती हैं।

+0

यह मौजूद है :(मैं https://stackoverflow.com/questions/23584160/correct-and-crossplatform-way-to-use-utf- में एक वर्कअराउंड खोजने की कोशिश कर रहा हूं। 8-इन-सी-स्ट्रीम आप का स्वागत होगा :) – eraxillan

1

ओई। कंसोल के कोड पेज को अपने प्रोग्राम के अंदर से बदलने का तरीका ढूंढने पर बधाई। मुझे उस कॉल के बारे में पता नहीं था, मुझे हमेशा chcp का उपयोग करना पड़ता था।

मुझे लगता है कि सी ++ डिफ़ॉल्ट लोकेल शामिल हो रहा है। डिफ़ॉल्ट रूप से यह गैर-wstring सामग्री के टेक्स्ट एन्कोडिंग को निर्धारित करने के लिए GetThreadLocale() द्वारा प्रदान किए गए कोड पेज का उपयोग करेगा। यह आमतौर पर सीपी 1252 के लिए डिफ़ॉल्ट है। आप UTF-8 (यदि यह भी ऐसा करता है, याद नहीं कर सकता) प्राप्त करने के लिए SetThreadLocale() का उपयोग करने का प्रयास कर सकता है, उम्मीद है कि std :: locale आपके यूटीएफ -8 एन्कोडिंग को संभालने वाली किसी चीज़ पर डिफ़ॉल्ट हो।

+0

निश्चित रूप से कोई समाधान नहीं है, लेकिन एक चीज जिसे मैंने पहले नहीं सोचा था। मैं कोशिश करूंगा कि जब मैं कुछ दिनों में काम पर वापस आऊंगा (घर पर मैं लिनक्स का उपयोग कर रहा हूं ...)। – mkluwe

+0

मैंने इसे फिर से देखा, लेकिन SetThreadLocale एन्कोडिंग से निपटता नहीं है, या मैं प्रलेखन को समझ नहीं पा रहा हूं http://msdn.microsoft.com/en-us/library/dd374051(VS.85).aspx। मैंने std :: cout.imbue के साथ थोड़ा सा प्रयास किया लेकिन इसका कोई फायदा नहीं हुआ। यह समस्या अनसुलझा बनी हुई है ... – mkluwe

3

मुझे समझ में आता है कि सवाल काफी पुराना है, लेकिन अगर कोई दिलचस्पी लेता है, तो मेरा समाधान नीचे है। मैंने एक बहुत ही सरल std :: streambuf वंशज को लागू किया है और फिर इसे प्रोग्राम निष्पादन की शुरुआत में मानक धाराओं में से प्रत्येक को पास कर दिया है।

यह आपको अपने कार्यक्रम में हर जगह यूटीएफ -8 का उपयोग करने की अनुमति देता है। इनपुट पर, यूनिकोड में कंसोल से डेटा लिया जाता है और फिर परिवर्तित किया जाता है और यूटीएफ -8 में आपके पास वापस आ जाता है। आउटपुट पर विपरीत किया जाता है, यूटीएफ -8 में आपके द्वारा डेटा लेना, इसे यूनिकोड में परिवर्तित करना और कंसोल पर भेजना। अभी तक कोई समस्या नहीं मिली है।

यह भी ध्यान दें कि इस समाधान को SetConsoleCP, SetConsoleOutputCP या chcp, या कुछ और के साथ किसी भी कोडपृष्ठ संशोधन की आवश्यकता नहीं है।

धारा बफर है कि:

class ConsoleStreamBufWin32 : public std::streambuf 
{ 
public: 
    ConsoleStreamBufWin32(DWORD handleId, bool isInput); 

protected: 
    // std::basic_streambuf 
    virtual std::streambuf* setbuf(char_type* s, std::streamsize n); 
    virtual int sync(); 
    virtual int_type underflow(); 
    virtual int_type overflow(int_type c = traits_type::eof()); 

private: 
    HANDLE const m_handle; 
    bool const m_isInput; 
    std::string m_buffer; 
}; 

ConsoleStreamBufWin32::ConsoleStreamBufWin32(DWORD handleId, bool isInput) : 
    m_handle(::GetStdHandle(handleId)), 
    m_isInput(isInput), 
    m_buffer() 
{ 
    if (m_isInput) 
    { 
     setg(0, 0, 0); 
    } 
} 

std::streambuf* ConsoleStreamBufWin32::setbuf(char_type* /*s*/, std::streamsize /*n*/) 
{ 
    return 0; 
} 

int ConsoleStreamBufWin32::sync() 
{ 
    if (m_isInput) 
    { 
     ::FlushConsoleInputBuffer(m_handle); 
     setg(0, 0, 0); 
    } 
    else 
    { 
     if (m_buffer.empty()) 
     { 
      return 0; 
     } 

     std::wstring const wideBuffer = utf8_to_wstring(m_buffer); 
     DWORD writtenSize; 
     ::WriteConsoleW(m_handle, wideBuffer.c_str(), wideBuffer.size(), &writtenSize, NULL); 
    } 

    m_buffer.clear(); 

    return 0; 
} 

ConsoleStreamBufWin32::int_type ConsoleStreamBufWin32::underflow() 
{ 
    if (!m_isInput) 
    { 
     return traits_type::eof(); 
    } 

    if (gptr() >= egptr()) 
    { 
     wchar_t wideBuffer[128]; 
     DWORD readSize; 
     if (!::ReadConsoleW(m_handle, wideBuffer, ARRAYSIZE(wideBuffer) - 1, &readSize, NULL)) 
     { 
      return traits_type::eof(); 
     } 

     wideBuffer[readSize] = L'\0'; 
     m_buffer = wstring_to_utf8(wideBuffer); 

     setg(&m_buffer[0], &m_buffer[0], &m_buffer[0] + m_buffer.size()); 

     if (gptr() >= egptr()) 
     { 
      return traits_type::eof(); 
     } 
    } 

    return sgetc(); 
} 

ConsoleStreamBufWin32::int_type ConsoleStreamBufWin32::overflow(int_type c) 
{ 
    if (m_isInput) 
    { 
     return traits_type::eof(); 
    } 

    m_buffer += traits_type::to_char_type(c); 
    return traits_type::not_eof(c); 
} 

उपयोग तो इस प्रकार है:

template<typename StreamT> 
inline void FixStdStream(DWORD handleId, bool isInput, StreamT& stream) 
{ 
    if (::GetFileType(::GetStdHandle(handleId)) == FILE_TYPE_CHAR) 
    { 
     stream.rdbuf(new ConsoleStreamBufWin32(handleId, isInput)); 
    } 
} 

// ... 

int main() 
{ 
    FixStdStream(STD_INPUT_HANDLE, true, std::cin); 
    FixStdStream(STD_OUTPUT_HANDLE, false, std::cout); 
    FixStdStream(STD_ERROR_HANDLE, false, std::cerr); 

    // ... 

    std::cout << "\xc3\xbc" << std::endl; 

    // ... 
} 

बाहर wstring_to_utf8 बाएँ और utf8_to_wstring आसानी से WideCharToMultiByte और MultiByteToWideChar WinAPI कार्यों के साथ लागू किया जा सकता।

+0

यह एक उपयोगी विचार था। आउटपुट के लिए मैं 'std :: stringbuf' से व्युत्पन्न कक्षा के साथ समाप्त हुआ (इसलिए मुझे अपने द्वारा बफरिंग करने की ज़रूरत नहीं है) और अभी रूपांतरण को 'सिंक()' लागू किया गया है। हार्ड-वायरिंग के बजाय कोड में आउटपुट सिंक, मेरा 'सिंक()' कनवर्ट स्ट्रिंग को धाराओं में मूल स्ट्रीमबफ में डाल देता है। – mkluwe