2016-10-13 2 views
3

Ich habe eine Zeichenfolge, die sowohl Mandarin und englische Wörter in UTF-8 enthält:wie eine chinesische Wörter und Englisch Wörter Mischung Zeichenfolge in c Sprache auszuschneiden

char *str = "你a好测b试"; 

Wenn Sie strlen(str) verwenden, wird es 14 zurückkehren , weil jedes Mandarinzeichen drei Bytes verwendet, während jedes englische Zeichen nur ein Byte verwendet.

Jetzt möchte ich die am weitesten links 4 Zeichen ("你a好测") kopieren, und am Ende "..." anhängen, um "你a好测..." zu geben.

Wenn der Text in einer Single-Byte-Codierung ist, kann ich nur schreiben:

strncpy(buf, str, 4); 
strcat(buf, "..."); 

Aber 4 Zeichen in UTF-8 ist nicht unbedingt 4 Byte. In diesem Beispiel sind es 13 Bytes: jeweils drei für , und und eine für a. Also, für diesen speziellen Fall würde ich

strncpy(buf, str, 13); 
strcat(buf, "..."); 

brauche Wenn ich einen falschen Wert für die Länge hatte, habe ich einen gebrochenen UTF-8-Stream mit einem unvollständigen Charakter erzeugen konnte. Offensichtlich möchte ich das vermeiden.

Wie kann ich die richtige Anzahl von zu kopierenden Bytes berechnen, entsprechend einer bestimmten Anzahl von Zeichen?

+0

ich nehme an, Sie eine UTF-8-Codierung haben, nicht wahr? –

+0

Wollte auf "你 a 好 测" schneiden? mit einem Buchstaben a? – Danh

+2

Was sind Ihre Kriterien für den Schneideplatz? Sie möchten eine definierte Anzahl von gültigen Codepunkte (in richtig darstellbaren Glyphen umgewandelt werden) nach links, dann der Rest ist drei-Punkte-and-a-null-Zeichen? Oder hat etwas mit dem Vorhandensein von Nicht-Multibyte-Zeichen zu tun? Oder was? –

Antwort

0

Pure-C-Lösung:

Alle UTF8 multibyte characters will be made from char-s with the most-significant-bit set to 1 mit den ersten Bits ihrer ersten Zeichen, das angibt, wie viele Zeichen macht einen Codepunkt.

Die Frage ist mehrdeutig in Bezug auf das Kriterium beim Schneiden; entweder:

  1. eine feste Anzahl von Codepoints von drei Punkten gefolgt, diese einen Puffer variabler Größe Ausgabe erfordert wil

  2. eine feste Ausgangspuffergröße, die verhängen „was auch immer Sie innen passen“

Beide Lösungen werden eine Hilfsfunktion zu sagen benötigen, wie viele Zeichen das nächste Codepoint machen:

// Note: the function does NOT fully validate a 
// UTF8 sequence, only looks at the first char in it 
int codePointLen(const char* c) { 
    if(NULL==c) return -1; 
    if((*c & 0xF8)==0xF0) return 4; // 4 ones and one 0 
    if((*c & 0xF0)==0xE0) return 3; // 3 ones and one 0 
    if((*c & 0xE0)==0xC0) return 2; // 2 ones and one 0 
    if((*c & 0x7F)==*c ) return 1; // no ones on msb 
    return -2; // invalid UTF8 starting character 
} 

Also, Lösung für das Kriterium 1 (feste Anzahl der Codepunkte, Variable Ausgang Buff Größe) - nicht ... an das Ziel anhängen, aber Sie können fragen, "wie viele Zeichen ich brauche" im Voraus und wenn es länger ist als Sie können leisten Sie sich, reservieren Sie sich den zusätzlichen Platz.

// returns the number of chars used from the output 
// If not enough space or the dest is null, does nothing 
// and returns the lenght required for the output buffer 
// Returns negative val if the source in not a valid UTF8 
int copyFirstCodepoints(
    int codepointsCount, const char* src, 
    char* dest, int destSize 
) { 
    if(NULL==src) { 
    return -1; 
    } 
    // do a cold run to see if size of the output buffer can fit 
    // as many codepoints as required 
    const char* walker=src; 
    for(int cnvCount=0; cnvCount<codepointsCount; cnvCount++) { 
    int chCount=codePointLen(walker); 
    if(chCount<0) { 
     return chCount; // err 
    } 
    walker+=chCount; 
    } 
    if(walker-src < destSize && NULL!=dest) { 
    // enough space at destination 
    strncpy(src, dest, walker-src); 
    } 
    // else do nothing 
    return walker-src; 
} 

Zweites Kriterium (begrenzte Puffergröße): Verwenden Sie nur die erste mit der Anzahl der Codepunkte zurück von diesem einen

// return negative if UTF encoding error 
int howManyCodepointICanFitInOutputBufferOfLen(const char* src, int maxBufflen) { 
    if(NULL==src) { 
    return -1; 
    } 
    int ret=0; 
    for(const char* walker=src; *walker && ret<maxBufflen; ret++) { 
    int advance=codePointLen(walker); 
    if(advance<0) { 
     return src-walker; // err because negative, but indicating the err pos 
    } 
    // look on all the chars between walker and walker+advance 
    // if any is 0, we have a premature end of the source 
    while(advance>0) { 
     if(0==*(++walker)) { 
     return src-walker; // err because negative, but indicating the err pos 
     } 
     advance--; 
    } // walker is set on the correct position for the next attempt 
    } 
    return ret; 
} 
+0

Er wollte schneiden, „einen 好 你 测“ mit einem Buchstaben a – Danh

+0

nein, ich mag eine Zeichenfolge der Größe zu begrenzen, die String-Chinesisch und Englisch Worte enthält, ist die Frage, wie man die richtige Postion zu finden chinesisches Wort verstümmelt werden zu vermeiden . Ich finde den richtigen Weg. – iverhan

+0

@iverhan um den String zu begrenzen? –

2

Zuerst müssen Sie Ihre Codierung kennen. Durch den Klang davon (3 Byte Mandarin) wird Ihre Zeichenfolge mit UTF-8 codiert.

Was Sie tun müssen, ist die UTF-8 wieder in Unicode-Codestellen (Integer) konvertieren.Sie können dann ein Array von Ganzzahlen anstelle von Bytes haben, so dass jedes Element des Arrays 1 Zeichen lang ist, unabhängig von der Sprache.

Sie auch eine Bibliothek von Funktionen nutzen könnten, die bereits utf8 wie http://www.cprogramming.com/tutorial/utf8.c http://www.cprogramming.com/tutorial/utf8.h

Insbesondere dieser Funktion umgehen: int u8_toucs(u_int32_t *dest, int sz, char *src, int srcsz); könnte sehr nützlich sein, wird es eine Reihe von ganzen Zahlen, wobei jede ganze Zahl 1 Charakter erstellen . Anschließend können Sie das Array ändern, wie Sie sehen, passen, dann ist es wieder zurück konvertieren mit int u8_toutf8(char *dest, int sz, u_int32_t *src, int srcsz);

+0

Für die utf8-to-ucs4-Konvertierung werden keine Drittanbieterbibliotheken benötigt, die Standardbibliothek ist dazu in der Lage. –

+0

Welche Funktionen der Standardbibliothek? Ist das nicht Teil von fontconfig, nicht Standard-Bibliothek? –

+1

In C++ ist es std :: wstring_convert, in c kann man mbstowcs (nicht überall, aber es funktioniert auf Linux gut). –

1

Die Basic Multilingual Plane wurde entwickelt, um Zeichen für fast alle modernen Sprachen enthalten. Insbesondere enthält es Chinesisch.

So müssen Sie nur noch Ihre UTF8-String in einem ein UTF16 konvertiert jedes Zeichen zu haben eine einzige Position mit. Das bedeutet, dass Sie nur einen wchar_t Array verwenden können, oder noch besser ein wstring nativ alle String-Funktionen verwenden zu dürfen.

Beginnend mit C++ 11 deklariert der Header <codecvt> einen dedizierten Konverter std::codecvt_utf8, um UTF8 schmale Strings speziell in breite Unicode-Strings umzuwandeln. Ich muss zugeben, es ist nicht sehr einfach zu bedienen, aber es sollte hier genug sein. Code könnte wie:

char str[] = "你a好测b试"; 
std::codecvt_utf8<wchar_t> cvt; 
std::mbstate_t state = std::mbstate_t(); 

wchar_t wstr[sizeof(str)] = {0}; // there will be unused space at the end 
const char *end; 
wchar_t *wend; 

auto cr = cvt.in(state, str, str+sizeof(str), end, 
     wstr, wstr+sizeof(str), wend); 
*wend = 0; 

Sobald Sie haben die wstr breite Zeichenfolge, können Sie es zu einem wstring umwandeln kann und alle C++ Bibliothek Tools verwenden, oder wenn Sie C-Strings bevorzugen, können Sie die ws... Kollegen von der str... verwenden Funktionen.

+0

Die Frage ist markiert [tag: c], nicht [tag: C++]. –

+0

@TobySpeight: Als ich antwortete, waren beide Tags vorhanden ... –

+0

Ah, ja - ich sehe das jetzt in der Bearbeitungsgeschichte. Wenn es nur einen effektiveren Weg gäbe, die Fragesteller davon abzuhalten, das zu tun ... –

0
static char *CutStringLength(char *lpszData, int nMaxLen) 
{ 
    if (NULL == lpszData || 0 >= nMaxLen) 
    { 
      return ""; 
    } 
    int len = strlen(lpszData); 
    if(len <= nMaxLen) 
    { 
      return lpszData; 
    } 
    char strTemp[1024] = {0}; 
    strcpy(strTemp, lpszData); 
    char *p = strTemp; 
    p = p + (nMaxLen-1); 

    if ((unsigned char)(*p) < 0xA0) 
    { 
     *(++p) = '\0'; // if the last byte is Mandarin character 
    } 
    else if ((unsigned char)(*(--p)) < 0xA0) 
    { 
     *(++p) = '\0'; // if the last but one byte is Mandarin character 
    } 
    else if ((unsigned char)(*(--p)) < 0xA0) 
    { 
     *(++p) = '\0'; // if the last but two byte is Mandarin character 
    } 
    else 
    { 
     int i = 0; 
     p = strTemp; 
     while(*p != '\0' && i+2 <= nMaxLen) 
     { 
      if((unsigned char)(*p++) >= 0xA0 && (unsigned char)(*p) >= 0xA0) 
      { 
       p++; 
       i++; 
      } 
      i++; 
     } 
     *p = '\0'; 
    } 
    printf("str = %s\n",strTemp); 
    return strTemp; 
} 
+0

Obwohl dieser Code helfen kann, das Problem zu lösen, erklärt er nicht, warum und/oder wie er die Frage beantwortet. Die Bereitstellung dieses zusätzlichen Kontextes würde seinen langfristigen Bildungswert erheblich verbessern. Bitte [bearbeiten] Sie Ihre Antwort, um eine Erläuterung hinzuzufügen, einschließlich der Einschränkungen und Annahmen. –

1

würde ich empfehlen, auf einer höheren Abstraktionsebene mit Dingen tun: entweder konvertiere wchar_t oder eine UTF-8-Bibliothek verwenden. Aber wenn Sie es wirklich auf Byte-Ebene tun möchten, können Sie Zeichen durch das Überspringen über die Fortsetzung Bytes (die von der Form sind 10xxxxxx) zählen:

#include <stddef.h> 

size_t count_bytes_for_chars(const char *s, int n) 
{ 
    const char *p = s; 
    n += 1; /* we're counting up to the start of the subsequent character */ 

    while (*p && (n -= (*p & 0xc0) != 0x80)) 
     ++p; 
    return p-s; 
} 

Hier ist eine Demonstration der obigen Funktion:

#include <string.h> 
#include <stdio.h> 
int main() 
{ 
    const char *str = "你a好测b试"; 
    char buf[50]; 
    int truncate_at = 4; 

    size_t bytes = count_bytes_for_chars(str, truncate_at); 
    strncpy(buf, str, bytes); 
    strcpy(buf+bytes, "..."); 

    printf("'%s' truncated to %d characters is '%s'\n", str, truncate_at, buf); 
} 

Ausgang:

'你a好测b试' truncated to 4 characters is '你a好测...' 
Verwandte Themen