2015-08-11 13 views
6

Chcę uzyskać podciąg o danej długości, powiedzmy 150. Jednak chcę się upewnić, że nie odcinam łańcucha pomiędzy znakami Unicode.Bezpieczne podstruktury UTF-16 w C# .NET

np. zobacz następujący kod:

var str = "Hello world!"; 
var substr = str.Substring(0, 6); 

Tutaj substr jest nieprawidłowy ciąg od Smiley znak jest przecięty na pół.

Zamiast chcę funkcję, która robi, co następuje:

var str = "Hello world!"; 
var substr = str.UnicodeSafeSubstring(0, 6); 

gdzie substr zawiera "Hello"

Dla porównania, tutaj jest to, jak byłoby to zrobić w Objective-C za pomocą rangeOfComposedCharacterSequencesForRange

NSString* str = @"Hello world!"; 
NSRange range = [message rangeOfComposedCharacterSequencesForRange:NSMakeRange(0, 6)]; 
NSString* substr = [message substringWithRange:range]]; 

Jaki jest równoważny kod w C#?

+0

@Eser UTF-16 może mieć 2 lub nawet 3 znaki. Więc tak, możesz przeciąć je na pół. –

+0

@Eser przeczytać https://msdn.microsoft.com/en-us/library/system.string(v=vs.110).aspx#Characters Char jest codepoint, znak Unicode może zawierać więcej niż 1 Char. Dla np. to 0xD83D 0xDE03, czyli 2 16-bitowe znaki. –

+0

Nie rozumiem, co powinna funkcja substr zrobić ... w "Hello", co to jest ''? A jak ma działać z [łącząc znaki] (https://en.wikipedia.org/wiki/Combining_character)? (więc, na przykład, możesz mieć 'a +' jeśli podzielisz go, otrzymasz 'a' bez znaku diakrytycznego ... – xanatos

Odpowiedz

4

ten powinien powrócić maksymalnej podciąg zaczynając od indeksu startIndex i długości do length z „Complete” grafemy ... Więc początkowy/Ostateczne "podzielone" pary zastępcze zostaną usunięte, początkowe znaki łączące zostaną usunięte, a ostateczne znaki, które nie zawierają ich znaków łączących, zostaną usunięte.

Zauważ, że prawdopodobnie nie jest to o co prosiłeś ... Wydaje się, że chcesz używać grafemów jako jednostki miary (a może chcesz dołączyć ostatni grafem, nawet jeśli jego długość będzie przekraczać parametr length)

public static class StringEx 
{ 
    public static string UnicodeSafeSubstring(this string str, int startIndex, int length) 
    { 
     if (str == null) 
     { 
      throw new ArgumentNullException("str"); 
     } 

     if (startIndex < 0 || startIndex > str.Length) 
     { 
      throw new ArgumentOutOfRangeException("startIndex"); 
     } 

     if (length < 0) 
     { 
      throw new ArgumentOutOfRangeException("length"); 
     } 

     if (startIndex + length > str.Length) 
     { 
      throw new ArgumentOutOfRangeException("length"); 
     } 

     if (length == 0) 
     { 
      return string.Empty; 
     } 

     var sb = new StringBuilder(length); 

     int end = startIndex + length; 

     var enumerator = StringInfo.GetTextElementEnumerator(str, startIndex); 

     while (enumerator.MoveNext()) 
     { 
      string grapheme = enumerator.GetTextElement(); 
      startIndex += grapheme.Length; 

      if (startIndex > length) 
      { 
       break; 
      } 

      // Skip initial Low Surrogates/Combining Marks 
      if (sb.Length == 0) 
      { 
       if (char.IsLowSurrogate(grapheme[0])) 
       { 
        continue; 
       } 

       UnicodeCategory cat = char.GetUnicodeCategory(grapheme, 0); 

       if (cat == UnicodeCategory.NonSpacingMark || cat == UnicodeCategory.SpacingCombiningMark || cat == UnicodeCategory.EnclosingMark) 
       { 
        continue; 
       } 
      } 

      sb.Append(grapheme); 

      if (startIndex == length) 
      { 
       break; 
      } 
     } 

     return sb.ToString(); 
    } 
} 

Variant, który będzie po prostu to „ekstra” znaki na końcu fragmentu, jeśli to konieczne, aby całość grafem:

public static class StringEx 
{ 
    public static string UnicodeSafeSubstring(this string str, int startIndex, int length) 
    { 
     if (str == null) 
     { 
      throw new ArgumentNullException("str"); 
     } 

     if (startIndex < 0 || startIndex > str.Length) 
     { 
      throw new ArgumentOutOfRangeException("startIndex"); 
     } 

     if (length < 0) 
     { 
      throw new ArgumentOutOfRangeException("length"); 
     } 

     if (startIndex + length > str.Length) 
     { 
      throw new ArgumentOutOfRangeException("length"); 
     } 

     if (length == 0) 
     { 
      return string.Empty; 
     } 

     var sb = new StringBuilder(length); 

     int end = startIndex + length; 

     var enumerator = StringInfo.GetTextElementEnumerator(str, startIndex); 

     while (enumerator.MoveNext()) 
     { 
      if (startIndex >= length) 
      { 
       break; 
      } 

      string grapheme = enumerator.GetTextElement(); 
      startIndex += grapheme.Length; 

      // Skip initial Low Surrogates/Combining Marks 
      if (sb.Length == 0) 
      { 
       if (char.IsLowSurrogate(grapheme[0])) 
       { 
        continue; 
       } 

       UnicodeCategory cat = char.GetUnicodeCategory(grapheme, 0); 

       if (cat == UnicodeCategory.NonSpacingMark || cat == UnicodeCategory.SpacingCombiningMark || cat == UnicodeCategory.EnclosingMark) 
       { 
        continue; 
       } 
      } 

      sb.Append(grapheme); 
     } 

     return sb.ToString(); 
    } 
} 

ten powróci co pytasz "Hello world!".UnicodeSafeSubstring(0, 6) == "Hello".

+0

Wydaje mi się to rozsądne, chociaż liczyłem na funkcję wbudowaną. –

+0

@KostubDeshmukh Właśnie dodano wariant, który będzie zawierał dodatkowe znaki na końcu podciągu ... – xanatos

2

Wygląda na to, że chcesz podzielić ciąg znaków na grafemów, czyli na pojedynczych wyświetlanych znakach.

W takim przypadku masz poręczny sposób: StringInfo.SubstringByTextElements:

var str = "Hello world!"; 
var substr = new StringInfo(str).SubstringByTextElements(0, 6); 
+2

Jedyną ważną rzeczą do zapamiętania jest to, że zarówno '0' jak i' 6' są w jednostkach tekstowych, a nie w znakach ... Jeśli 'str ==" "(gdzie każdy glif jest 2 znaki),' substr' będzie "" ", więc' substr.Length == 12' – xanatos

+1

Jak wspomniano o @xanatos, nie rozwiązuje to problemu, gdy wszystkie pierwsze 6 grafemów to 2 znaki. Nadal chcę, aby długość wynosiła 6 + dodatkowe punkty kodowe wymagane dla ostatniego grafem, to znaczy "" w przypadku wspomnianych xanatos. –

+0

OK, źle zrozumiałem wtedy problem. –

Powiązane problemy