2016-03-29 10 views
3

Geniş char dizesini biçimlendirmek ve std :: wstring öğesini döndürmek için bir işlev uygulamanız gerekir. Benim uygulamasıdır:OSX'deki vswprintf ile geniş char dizgisi nasıl biçimlendirilir (std :: wstring öğesini döndürmek için)

std::wstring format(const wchar_t* fmt, ...) 
{ 
    std::wstring ret; 

    va_list va; 
    va_start(va, fmt); 

    int size = vswprintf(nullptr, 0, fmt, va); 
    if (size > 0) 
    { 
     ret.resize(size + 1); 
     vswprintf(&ret[0], size + 1, fmt, va); 
    } 

    va_end(va); 
    return ret; 
} 

o pencerelerde iyi çalışır, ancak vswprintf (nullptr, 0, bicim, va) dönecektir çünkü ne yazık ki osx çalışmıyor -1.

yalnızca std kullanılabilir ve dize uzunluğunun belirli bir aralıkta olduğunu kabul edemiyorum.

Bunu yapmanın bir yolu olmalı, ama bulamıyorum, yardımcı olabilir misiniz? teşekkürler

+0

"nullptr" yerine okunaklı bir işaretçi vermeye çalışabilir misiniz? Belki de işaretçi geçerliliğinin ekstra kontrolü vardır. – SHR

+0

@SHR ([std :: vswprintf] (http://en.cppreference.com/w/cpp/io/c/vfwprintf) return -1 –

+2

(OS X'te) gözlemlemek ne için belgelenmiş bir davranıştır, işe * "[...] üretilecek karakter sayısı, boyutuna eşit veya daha büyükse," dönüş değeri [bir] negatif değerdir. "*). Bu arabellek, son biçimlendirilmiş dizeyi tutacak kadar büyük olana ve bir arabellek üzerine bir 'std :: wstring' döndürene kadar, döngüsel bir yerel arabelleğe okumaya devam etmelisiniz. Ne yazık ki, Microsoft'un [_vscwprintf] (https://msdn.microsoft.com/en-us/library/w05tbk72.aspx) uzantısının standart bir uygulaması yoktur. – IInspectable

cevap

0

yalancı kod: Başka bir deyişle

buffer.resize(1) 
loop: 
    res = vswprintf(buffer, args) 
    if res indicates success: 
     break 
    buffer.resize(buffer.size() * 2) 
buffer.truncate() 

, sadece başarılı dek tampon boyutları arttıkça deneyin. Temelde 1 char'dan daha büyük bir şeyle başlıyorum, temelde çoğu çağrı tekrarlanmak zorunda değil, ama bu sadece performans fineting.

+0

ben bu şekilde kabul var, teşekkür ederim, ama bu vswprintf Dürüst olmak gerekirse başka bir nedenden –

+0

için başarısız olabilir çünkü sonsuz bir döngüye olması mümkün değil. Eğer değilse, getiri değeri başarı (break), hata (throw) ve truncation (tekrarlama) arasında ayrım yapmanızı sağlar. –

+0

'buffer.truncate()' gerekli değildir. 'vswprintf' zaten geçerli değerlerini dönüş değeriyle sağladı. Tamponu üssel olarak büyütmek genellikle iyi bir seçenek değildir. Bunun yerine doğrusal olarak büyütün. – IInspectable

1

Görünüşe göre, Darwin sürümü aslında according to the specification for vfwprintf davranıyor. Ne yazık ki, bir geniş formatlı dizginin uzunluğunu düzenli bir tek bayt dizgisi (NULL hedef dizgisini geçirerek) ile aynı şekilde hesaplamak için uygun bir yol yoktur.

çok araştırma sonra ben bu cevapta belirtildiği her ikisi de iki uygulanabilir çözümler, görünmektedir orada bulduk: göründüğü gibi

  • How to determine buffer size for vswprintf under Linux gcc
    • Ben iteratif tahsisi yaklaşımı sevmediğim Yeterince tahsis edene kadar aynı işlemi birkaç kez körü körüne teşebbüs etmek için verimsiz.

      Tercih ettiğim çözüm, uzunluğu hesaplamak için dizeyi (bir kez!) /dev/null'a biçimlendirmek ve sonra arabelleğinizi boyutlandırmak için bunu kullanmaktır. Vs * işlevleri tarafından tüketildiği için bunu yapmak için değişken dağıtım listelerinin kopyalanması gerekir. Bu, null yaptığınız şeye en yakın olanıdır, sadece boş bir dize yerine boş bir akışla.

      Benim çözüm önerisi böylece:

      std::wstring format(const wchar_t* fmt, ...) 
      { 
          std::wstring ret; 
      
          va_list va; 
          va_start(va, fmt); 
      
          va_list vc; 
          va_copy(vc, va); 
      
          FILE* fp = fopen("/dev/null", "w"); 
          int size = vfwprintf(fp, fmt, vc); 
          fclose(fp); 
      
          if (size > 0) 
          { 
           ret.resize(size + 1); 
           vswprintf(&ret[0], size + 1, fmt, va); 
          } 
      
          va_end(va); 
          va_end(vc); 
      
          return ret; 
      } 
      

      Aşağıdaki kod ile Mac OS X üzerinde test ettik ve beklenen çıkışı gözlenen:

      int main(int argc, char* argv[]) 
      { 
          auto outstr = format(L"Hello %ls, you are %u years old.\n", L"Fred", 65); 
      
          std::wcout << outstr << std::endl; 
      
          return 0; 
      } 
      
    +0

    @IInspectable ... –

    +0

    '/ dev/null /' çözüm yerine '/ dev' NUL' kullanarak, Windows için açıkçası –

    +0

    @MM olmayan taşınabilir böylece tüm derleyiciler uyumludur, tanımsız davranıştır/null' gayet iyi çalışmalı.Yukarıdaki bağlantılı konudaki poster tarafından onaylandı. – gavinb

    0

    Olası bir çözüm kullanmak olabilir C11 standart fonksiyon vswprintf_s fonksiyonu. Bu tamamen taşınabilir değildir çünkü C11 bu işlevin yerine getirilmesini isteğe bağlı yapar, ancak uygulamanızın bunu gerçekleştirmesi durumunda bunu anımsatır.

    C++ 14 yalnızca C99 standart kitaplığını belirtir ve bu işlev C11'dir. Ancak, C++ derleyicinizin C derleyicisinin C11'i desteklemesi durumunda, bu işlevin C++ 'dan doğrudan kullanılabilir hale gelmesi olasıdır. Olmasa bile, bu kodu bir C işlevine sarın ve C++ işlevini C++ olarak adlandırın.

    işlevi, vswprintf için farklı değişkenler kullanır, bu nedenle belgelerini dikkatlice kontrol edin.

    +0

    Teşekkürler. btw, vswprintf ve vswprintf_s arasında herhangi bir argüman farkı göremiyorum, http://en.cppreference.com/w/c/io/vfwprintf, ne demek istediniz? –

    İlgili konular