Je programme quelque chose qui compte le nombre de caractères UTF-8 dans un fichier. J'ai déjà écrit le code de base mais maintenant, je suis coincé dans la partie où les caractères sont censés être comptés. Jusqu'à présent, voici ce que j'ai:
Ce qu'il y a à l'intérieur du fichier texte:
#include <stdio.h> typedef unsigned char BYTE; int main(int argc, char const *argv[]) { FILE *file = fopen("file.txt", "r"); if (!file) { printf("Could not open file.\n"); return 1; } int count = 0; while(1) { BYTE b; fread(&b, 1, 1, file); if (feof(file)) { break; } count++; } printf("Number of characters: %i\n", count); fclose(file); return 0; }
Ce que j'ai codé jusqu'à présent:
é»åçè ä½ å¥½ ããã«ã¡ã¯ ì¬ë³´ì¸ì
Ma question est la suivante: comment coderais-je la partie où les caractères UTF-8 sont comptés? J'ai essayé de chercher des inspirations dans GitHub et YouTube, mais je n'ai encore rien trouvé qui fonctionne bien avec mon code.
Edit: à l'origine, ce code imprime que le fichier texte comporte 48 caractères. Mais compte tenu de l'UTF-8, il ne devrait comporter que 18 caractères.
4 Réponses :
En C, comme en C ++, il n'y a pas de solution toute faite pour compter les caractères UTF-8. Vous pouvez convertir UTF-8 en UTF-16 à l'aide de mbstowcs et utiliser la fonction wcslen , mais ce n'est pas le meilleur moyen pour les performances (surtout si vous n'avez besoin que de compter le nombre de caractères et rien d'autre).
Je pense qu'une bonne réponse à votre question est ici: compter les caractères Unicode en C ++ .
Ð • xample de la réponse sur le lien:
for (p; *p != 0; ++p) count += ((*p & 0xc0) != 0x80);
Vous pouvez consulter les spécifications: https://tools.ietf.org/html/rfc3629 .
Le chapitre 3 contient ce tableau:
Char. number range | UTF-8 octet sequence (hexadecimal) | (binary) --------------------+--------------------------------------------- 0000 0000-0000 007F | 0xxxxxxx 0000 0080-0000 07FF | 110xxxxx 10xxxxxx 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
Vous pouvez inspecter les octets et créer les caractères Unicode.
Un point différent est de savoir si vous comptez un caractère de base et son accent (marque de combinaison cf. https://en.wikipedia.org/wiki/Combining_character ) comme un ou comme plusieurs caractères.
Voir: https://en.wikipedia.org/wiki/UTF-8#Encoding
Chaque séquence UTF-8 contient un octet de départ et zéro ou plusieurs octets supplémentaires. Les octets supplémentaires commencent toujours par les bits 10
et le premier octet ne commence jamais par cette séquence. Vous pouvez utiliser ces informations pour compter uniquement le premier octet de chaque séquence UTF-8.
if((b&0xC0) != 0x80) { count++; }
Gardez à l'esprit que cela se cassera si le fichier contient des séquences UTF-8 non valides. De plus, les «caractères UTF-8» peuvent signifier des choses différentes. Par exemple, "ðŸ '© 🠿" sera compté comme deux caractères par cette méthode.
Je viens de lire le contenu du lien que vous avez joint. Cependant, je suis assez confus quant à la façon dont j'appliquerai ces choses dans mon code.
@thepajama Pour une question spécifique, créez-en une nouvelle ici sur StackOverflow. Indiquez le point exact qui commence votre confusion et indiquez les choses que vous avez comprises jusqu'à présent.
Vous pouvez choisir plusieurs options:
main1
UTF-8 en chaîne large par lui-même (voir main1
ci-dessous)main2
ci-dessous)main3
ci-dessous qui utilise libunistring
)utf8_strlen
votre propre solution utf8_strlen
qui fonctionnera sur une propriété de chaîne UTF-8 spécifique et vérifiez vous-même les octets, comme indiqué dans d'autres réponses. Voici un exemple de programme qui doit être compilé avec -lunistring
sous Linux avec une vérification rudimentaire des erreurs avec assert
:
#include <stdio.h> #include <wchar.h> #include <locale.h> #include <assert.h> #include <stdlib.h> void main1() { // read the file as wide characters const char *l = setlocale(LC_ALL, "en_US.UTF-8"); assert(l); FILE *file = fopen("file.txt", "r"); assert(file); int count = 0; while(fgetwc(file) != WEOF) { count++; } fclose(file); printf("Number of characters: %i\n", count); } // just a helper function cause i'm lazy char *file_to_buf(const char *filename, size_t *strlen) { FILE *file = fopen(filename, "r"); assert(file); size_t n = 0; char *ret = malloc(1); assert(ret); for (int c; (c = fgetc(file)) != EOF;) { ret = realloc(ret, n + 2); assert(ret); ret[n++] = c; } ret[n] = '\0'; *strlen = n; fclose(file); return ret; } void main2() { const char *l = setlocale(LC_ALL, "en_US.UTF-8"); assert(l); size_t strlen = 0; char *str = file_to_buf("file.txt", &strlen); assert(str); // convert multibye string to wide string // assuming multibytes are in UTF-8 // this may also be done in a streaming fashion when reading byte by byte from a file // and calling with `mbtowc` and checking errno for EILSEQ and managing some buffer mbstate_t ps = {0}; const char *tmp = str; size_t count = mbsrtowcs(NULL, &tmp, 0, &ps); assert(count != (size_t)-1); printf("Number of characters: %zu\n", count); free(str); } #include <unistr.h> // u8_mbsnlen from libunistring void main3() { size_t strlen = 0; char *str = file_to_buf("file.txt", &strlen); assert(str); // for simplicity I am assuming uint8_t is equal to unisgned char size_t count = u8_mbsnlen((const uint8_t *)str, strlen); printf("Number of characters: %zu\n", count); free(str); } int main() { main1(); main2(); main3(); }
Vous pouvez suivre les règles d'encodage . Si multi-octets, continuez à lire le bon nombre d'octets pour satisfaire l'encodage des caractères.
Voulez-vous également vérifier le codage UTF-8 ou voulez-vous supposer que les données sont valides UTF-8?
Je veux supposer que les données sont valides UTF-8.
fread(&b, 1, 1, file); if (feof(file))
- fait amusant, s'il y a une erreur lors de la lecture du fichier, votre code boucle sans fin. Oubliezfeof
existe! Vérifiez la valeur de retour des fonctions, defread
, pour déterminer si votre opération a réussi.if (fread(&b, 1, 1, file) != 1) { break; }
Mode pédantique: vous comptez les points de code Unicode (les points de code de contrôle ne sont pas toujours des «caractères Unicode», par exemple une nouvelle ligne), et il est contestable si une variante compte pour un ou deux caractères.