Présentation des caractères et des chaînes en langage C pour Swift Programmer

Environnement de vérification

Terminal: MacBook Air OS: macOS High Sierra Swift: 4.0.3 (swiftlang-900.0.74.1 clang-900.0.39.2) Clang: Apple LLVM version 9.0.0 (clang-900.0.39.2)

Aperçu

Je pense que tout le monde utilise avec désinvolture la méthode suivante lors de la conversion d'une chaîne C (char \ *) en une chaîne Swift ou Objective-C (String, NSString).

String.swift


init?(cString: UnsafePointer<CChar>) //typealias CChar = Int8

Tout d'abord, je voudrais présenter le cString qui est passé à cette méthode, c'est-à-dire la chaîne C. Ensuite, en fait, la méthode ci-dessus a été abolie du côté NSString, et une méthode pour spécifier le codage en même temps est préparée comme indiqué ci-dessous. Je voudrais réfléchir à la valeur d'encodage passée à ce moment.

String.swift


init?(cString: UnsafePointer<CChar>, encoding enc: String.Encoding)

À propos de la lettre C

«Char» est fourni comme un type qui représente un caractère en langage C. Une valeur de 1 octet peut être stockée dans cette variable de type char. La lettre C entoure la lettre entre guillemets simples. Et la valeur entre guillemets simples est appelée littéral de caractère.

Lettre C.c


int main(void) {
    char c = '*'; //「'Il est converti en une valeur ASCII 1 octet en entourant le caractère d'une "marque".
    printf("%c\n", c); // *
    printf("%ld\n", sizeof(c)); // 1

}

Comme mentionné dans les commentaires, ce qui précède est en fait la syntaxe d'enrobage de sucre suivante.

lettre c.c


int main(void) {
    char c = 42;
    printf("%c\n", c); // *
    printf("%ld\n", sizeof(c)); // 1
}

En d'autres termes, la substance du caractère littéral est "juste un nombre".

De plus, si vous mettez des caractères multi-octets entre guillemets simples comme indiqué ci-dessous, le nombre généré (* 1) dépassera la taille de char (1 octet), ce qui entraînera une erreur de compilation.

(\ * 1: Pour la valeur générée, consultez la section ci-dessous "[Lorsque des caractères multi-octets sont stockés dans la chaîne C](https://qiita.com/ysn551/items/446074b22103233edd95#c%E3%81] % AE% E6% 96% 87% E5% AD% 97% E5% 88% 97% E3% 81% AB% E3% 83% 9E% E3% 83% AB% E3% 83% 81% E3% 83% 90 % E3% 82% A4% E3% 83% 88% E6% 96% 87% E5% AD% 97% E3% 82% 92% E6% A0% BC% E7% B4% 8D% E3% 81% 97% E3 % 81% 9F% E5% A0% B4% E5% 90% 88) ”)

Lettre C.c


//Ce fichier de code source est enregistré en UTF8
int main(void) {
    char c = 'Ah'; // error: character too large for enclosing character 
}

En d'autres termes, les caractères multi-octets ne peuvent pas être stockés car ils le sont dans des variables de type char.

De plus, la vérification que le littéral de caractère réel est un nombre à 1 octet peut être prouvée par le fait que la valeur peut être directement stockée dans le type int (4 octets) comme indiqué ci-dessous.

La substance du caractère littéral est un nombre de 1 octet.c


int main(void) {
    int num = 'abcd';
    printf("%0x\n", num); // 64656667
}

Le résultat de la sortie de la valeur de num en hexadécimal est "64656667", et si vous lisez la valeur en unités d'octets, vous pouvez voir qu'elle peut être décomposée en "64,65,66,67".

Résumé ici

À propos de la chaîne de caractères de C

La chaîne de langage C est un tableau de type «char». En d'autres termes, il est représenté par un tableau pour stocker des données à 1 octet. En outre, la chaîne de caractères C entoure le caractère entre guillemets. En outre, la valeur entre guillemets est appelée littéral de chaîne.

C chaîne.c


int main(void) {
    char str[] = "Hello"; //Le nombre d'éléments peut être omis en initialisant en même temps.
    printf("sizeof(str)/sizeof(char) = %ld\n", sizeof(str)/sizeof(char)); // 6
}

La chaîne de caractères * Hello * utilisée pour l'initialisation ci-dessus est de 5 caractères, mais le nombre d'éléments est de 6. En fait, il a la syntaxe d'enrobage de sucre suivante.

À propos de la chaîne de caractères de C.c


int main(void) {
    char str[] = {'H','e','l','l','o','\0'};
    printf("sizeof(str)/sizeof(char) = %ld\n", sizeof(str)/sizeof(char)); //6
}

En d'autres termes, la chaîne de caractères littérale "" Hello "` renvoie un tableau de caractères avec 6 éléments qui stocke le caractère nul à la fin.

Résumé ici

Lorsque des caractères multi-octets sont stockés dans la chaîne de caractères C

Bien que cela ne soit pas mentionné dans la section ci-dessus, les résultats lorsque le code source initialisé avec des caractères multi-octets comme indiqué ci-dessous est enregistré dans un fichier avec UTF8 et lorsqu'il est enregistré dans un fichier avec Shift-JIS sont affichés ci-dessous. J'aimerais voir ça.

À propos de la chaîne de caractères de C.c


int main(void) {
    char str[] = "Ah"; //Si vous déclarez un tableau en même temps que l'initialisation, vous pouvez omettre le nombre d'éléments
    int size = sizeof(str);
    for (int i = 0; i < size; i++) {
        printf("%hhx ", str[i]); //Validez cette sortie à chaque encodage
    }
}

Méthode de vérification:

  1. Ouvrez l'éditeur
  2. Modifiez le paramètre de codage de l'éditeur sur Shift-JIS ou UTF-8
  3. Collez le code source et enregistrez
  4. Compilez avec le compilateur clang (\ $ cc file.c)
  5. Exécutez (\ $ ./a.out)

Sortie lors de l'enregistrement en UTF8:

case_utf8_result.txt


e3 81 82 0 

Sortie lors de l'enregistrement sous Shift-JIS:

case_shift_jis_result.txt


82 a0 0

Pour chacune des valeurs ci-dessus, entrez «A» dans ce Site et affichez le résultat. résultat

En d'autres termes, vous pouvez voir que le résultat du littéral de chaîne du caractère multi-octets en langage C correspond au codage de l'éditeur de texte.

C'est un résultat très naturel car nous transmettons le "fichier" dans lequel le code source est écrit au compilateur, pas le "code source".

En d'autres termes, dans le cas de l'UTF8, on peut dire que char str [] =" a " est la syntaxe d'enrobage de sucre suivante.

À propos de la chaîne de caractères de C.c


int main(void) {
    //e3 81 82 0 
    char str[] = {0xe3, 0x81, 0x82, 0x0};
    printf("%s \n", str); //Si le paramètre de codage du terminal est UTF8, "A" s'affiche.
}

Si le terminal à exécuter ci-dessus est codé en UTF-8 et exécuté, "A" s'affiche. Si vous utilisez Shift-JIS, les caractères seront déformés. (Paramètres → Profils → Balise avancée) Screen Shot 2017-12-25 at 13.57.40.png

Par conséquent, le haut est le résultat lorsque Shift-JIS est défini et le bas est le résultat lorsque UTF8 est défini. Screen Shot 2017-12-25 at 13.56.20.png

Résumé ici

À propos du codage spécifié lors de la conversion en chaîne Swift

Je voudrais utiliser l'API Swift ci-dessous pour convertir les caractères passés de l'API C en une chaîne Swift. Quelle devrait être la valeur de codage spécifiée à ce moment?

String.swift


init?(cString: UnsafePointer<CChar>, encoding enc: String.Encoding) //CChar = Int8

Le code de programme C à vérifier est le suivant.

libc.c


char* file_name() {
    return "hello.txt";
}

char* new_file_header_str() {
    FILE *f = fopen(file_name(), "r");
    if (f == NULL) return NULL;

    char *str = calloc(256, sizeof(char));
    fgets(str, 256, f); //Une seule ligne
    fclose(f);
    return str;
}

Lors de l'appel de ce qui précède depuis Swift, le type C char * est passé en tant que type ʻUnsafeMutablePointer `.

Tout d'abord, je voudrais vérifier que le caractère C obtenu à partir de la fonction nom_fichier est converti en caractère Swift. Ce littéral de chaîne de caractères est renvoyé tel quel. En d'autres termes, vous pouvez voir que la valeur d'encodage lors de la conversion en une chaîne Swift doit être la même que l'encodage dans le fichier libc.c.

Ensuite, qu'en est-il de la valeur de codage utilisée pour convertir le caractère C obtenu à partir de la fonction new_file_header_str en une chaîne de caractères Swift? Ici, la chaîne de caractères du fichier hello.txt est renvoyée. Ainsi, vous pouvez voir que la valeur d'encodage que vous devez spécifier ici doit être la même que la valeur d'encodage où le fichier hello.txt est stocké.

Vous trouverez ci-dessous un exemple de code source qui enregistre le fichier lib.c en UTF-8 et le fichier hello.txt en Shift-JIS et appelle chaque fonction depuis Swift.

get_str_from_c.swift


let name = file_name() //Optional<UnsafeMutablePointer<Int8>>
if let name = name,
    let converted = String(cString: name, encoding: .utf8) {
    print(converted)
} 

let header = new_file_header_str() //Optional<UnsafeMutablePointer<Int8>>
if let header = header,
    let converted = String(cString: header, encoding: .shiftJIS) {
    print(converted)
}

Veuillez vous référer à ce qui suit pour appeler la bibliothèque C. https://qiita.com/ysn551/items/83e06cf74ae628cb573c

Résumé ici

Chaîne littérale Python3

De cette façon, le littéral de chaîne C stocke directement la valeur de codage, donc cela dépend de l'environnement de développement. D'ailleurs, dans le cas du compilateur Swift, seuls les fichiers UTF8 peuvent être compilés.

En revanche, dans Python3, la valeur générée par le littéral de chaîne est un nombre, mais il s'agit d'une valeur Unicode. Par conséquent, il n'est pas nécessaire de prendre en compte le codage lors de l'échange de littéraux de chaîne entre fichiers.

Le résultat de la vérification avec python3 est le suivant. À propos, Python2 utilise des valeurs d'encodage, il est donc inutile si l'encodage entre les codes sources est différent.

Enregistrez le fichier shift_jis.py suivant dans l'encodage Shift-JIS

shift_jis.py


#! coding=shift-jis

word = "Ravi de vous rencontrer"

Enregistrez le fichier utf8.py suivant sous le nom UTF8 et exécutez-le.

utf8.py


#! coding=utf-8

import shift_jis as sh

if sh.word == "Ravi de vous rencontrer": 
    print("true")
else:
    print("false")

Lorsque j'exécute ce qui précède sur python3, true s'affiche, mais sur python2, false s'affiche.

Résumé final

J'ai hâte de travailler avec vous en 2018. m (__) m

Recommended Posts

Présentation des caractères et des chaînes en langage C pour Swift Programmer
Macro C pour watchOS et disponibilité de l'API Swift
Macro C pour tvOS et disponibilité de l'API Swift
Traitez les fonctions et fermetures Swift comme des pointeurs de fonction en langage C
Créer UnsafeMutablePointer <UnsafeMutablePointer <Int8>?>! En Swift pour C char ** hoge
[Swift] Utiliser pour où plutôt que pour imbriquer pour et si