ubuntuusers.de

UTF-konforme Textdateien mit C erstellen

Status: Gelöst | Ubuntu-Version: Xubuntu 16.10 (Yakkety Yak)
Antworten |

GideonRavenor

Anmeldungsdatum:
1. März 2015

Beiträge: 171

Ich habe hier ein wunderschönes Programm geschrieben, das eine kleine Caesar-Verschlüsselung mit Zeichen und Ziffern durchführen soll. Klappt an sich, nur ist die erstellte Text-Datei immer in irgendeinem ISO-format. Wenn Ich sie mit Mousepad öffnen möchte, meldet das Programm "Dieses Dokument ist kein gültiges UTF-8". Ich muss dann einen der ISO-Standards auswählen, um die Datei anzeigen / öffnen zu können.

Außerdem wird in der ver- / entschlüsselten Datei immer ein komisches Zeichen "ÿ" angehängt - Ich vermute, dass da versehentlich das EOF noch mitgezogen wird, obwohl das feof-Funktion in der while eigentlich abfangen müsste.

Weiß da jemand Rat?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <stdio.h>

int encodeAlpha(int ch)
{	int rueck;
	
	if(ch < 'a')
		rueck = (ch-'A'+13)%26+'A';
	else
		rueck = (ch-'a'+13)%26+'a';
	
	return rueck;	
}

int encodeDigit(int ch)
{	return( ((ch - '0' +5) % 10 ) + '0' ); 
}

int main(int argc, char *argv[])
{	if(argc == 3)
	{	FILE *fpQ, *fpZ;
		char alpha, omega;
		int digitIn, digitOut;
		
		if( (fpQ = fopen(argv[1], "r")) != 0 && ((fpZ = fopen(argv[2], "w")) != 0) )
		{	
			while ( feof(fpQ) == 0)
			{	alpha = fgetc(fpQ);
				digitIn = (int)alpha;
				//printf("%c ", digitIn);
				
				if( (digitIn >= 'A' && digitIn <= 'Z') || (digitIn >= 'a' && digitIn <= 'z') )
					digitOut = encodeAlpha(digitIn);
				else if( digitIn >= '0' && digitIn <= '9')
					digitOut = encodeDigit(digitIn);
				else
					digitOut = digitIn;
				
				omega = (char)digitOut;
				fputc(omega, fpZ);
				//printf("%c ", omega);
			}
			
			fclose(fpQ);
			fclose(fpZ);
		}
		else
			fprintf(stderr, "Fehler, Datei konnte nicht geoffnet werden!\n");
		
	}
	else
		printf("Falscher Aufruf!\n<programmname> <quelldateiname> <zieldateiname>\n");
	
	return 0;
}

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11260

Wohnort: München

Schau mal hier: http://www.cprogramming.com/tutorial/unicode.html.

Das Problem dürfte sein, dass du durch den ROT-13 Werte für Zeichen bekommst, die sich nicht mehr mit 7 Bit (0-127) darstellen lassen. Und dann musst du berücksichtigen, dass UTF-8 Zeichen mit mehr als 7 Bit Informationen mit mehr als einem Byte darstellt (vgl. UTF-8).

Dakuan

Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6500

Wohnort: Hamburg

Ich habe hier ein wunderschönes Programm geschrieben, ...

Jo, eigene Programme sind immer wunderschön, jedenfalls solange sie nicht abstürzen 😉

Für die Umwandlung von Zeichencodes > 127 in UTF-8 sollte man vorzugsweise die entsprechenden Wide-Character Funktionen. Aber ich habe mich einmal darin versucht das selber zu lösen. Hier mal ein Auszug aus meiner Rezepte Sammlung:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/*
UTF-8 Codierung eines Unicode Zeichens. Diese Version ist nicht
thread-save wegen des statischen Puffers. Das kann aber bei Bedarf geändert
werden, indem der Anwender den Puffer (8 Bytes) bereit stellt.
Dabei ist zu beachten, das der zurückgelieferte Zeiger nicht auf den
Pufferanfang zeigt. Er darf also nicht für free() verwendet werden.
*/

/* ---------------------------------------------------------------------------
**      Encode an unicode character to an UTF-8 string
*/
char *
code2utf8( unsigned long code )
{
    static char     string[8];          /* storage for UTF-8 chars */
    int             i = 7;              /* index to last output char */
    unsigned long   val = code;
    unsigned long   startmax  = 0x3F;   /* max possible value in startbyte */
    unsigned int    startbits = 0x80;   /* one bit for every byte */

    string[ i ] = '\0';                 /* mark end of string */
    if ( val > 127  ) {                 /* do we need UTF-8 encoding? */
        while ( i > 0 ) {
            if ( val <= startmax ) {
                /* the remaining bits will fit into startbyte */
                string[ --i ] = val | startbits;
                break;
            }
            /* we will need another byte */
            string[ --i ] = (val & 0x3F) | 0x80;
            val >>= 6;
            startmax  >>= 1;
            startbits = (startbits >> 1) | 0x80;
        }
    } else {                            /* ASCII direct, no UTF-8 needed */
        string[ --i ] = val;
    }
    return string + i;
}

Vielleicht hilft das ja.

GideonRavenor

(Themenstarter)

Anmeldungsdatum:
1. März 2015

Beiträge: 171

Hm, aber wir verlassen in meinem Beispiel doch den zulässigen Bereich gar nicht? Alle Zeichen von a-z und A-Z werden wieder geschoben zu ganz normalen ASCII-Zeichen.

Dakuan

Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6500

Wohnort: Hamburg

Hm, aber wir verlassen in meinem Beispiel doch den zulässigen Bereich gar nicht?

Offenbar doch, denn

"Dieses Dokument ist kein gültiges UTF-8"

UTF-8 Strings haben immer das Minus-Bit gesetzt, wobei dabei immer bestimmte Muster eingehalten werden. Die Fehlermeldung lässt darauf schließen, das diese Muster nicht vorliegen. Ohne gesetztes Bit-7 würde dein Mousepad gar nicht auf die Idee kommen auf UTF-8 zu testen.

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11260

Wohnort: München

GideonRavenor schrieb:

Hm, aber wir verlassen in meinem Beispiel doch den zulässigen Bereich gar nicht? Alle Zeichen von a-z und A-Z werden wieder geschoben zu ganz normalen ASCII-Zeichen.

Stimmt, da kommt tatsächlich das EOF durch. Anscheinend bekommst du das als letztes Zeichen der Datei bevor feof() einen anderen Wert als 0 liefert.

Dakuan

Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6500

Wohnort: Hamburg

Stimmt, da kommt tatsächlich das EOF durch.

Ups, über den Fehler bin ich auch schon mal gestolpert. feof() liefert erst dann TRUE, wenn man über das Dateiende hinaus gelesen hat. Daher muss man eigentlich auch die Ausgabe von fgetc() auf den Wert EOF testen.

GideonRavenor

(Themenstarter)

Anmeldungsdatum:
1. März 2015

Beiträge: 171

Tatsache, das ist es. Warum mir das das Format zerschießt bzw. warum es dann als komisches Zeichen interpretiert wird erschließt sich mir zwar nicht ganz, aber sei's drum, hauptsache Fehler weg.

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11260

Wohnort: München

GideonRavenor schrieb:

Warum mir das das Format zerschießt bzw. warum es dann als komisches Zeichen interpretiert wird erschließt sich mir zwar nicht ganz, aber sei's drum, hauptsache Fehler weg.

1
2
3
4
5
#include <stdio.h>

void main() {
    printf("%c", EOF);
}

EOF wird bei mir zu ff und das ist mehr als sich mit 7 Bit darstellen lässt, und damit ein ungültiges mit nur einem Byte darstellbares Zeichen bei UTF-8:

./a.out | hexdump
0000000 00ff
0000002

Dakuan

Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6500

Wohnort: Hamburg

Warum mir das das Format zerschießt bzw. warum es dann als komisches Zeichen interpretiert wird erschließt sich mir zwar nicht ganz ...

EOF wird normalerweise als -1 (int) codiert, d.h. alle Bits an. Wenn das dann als Character interpretiert wird ergibt das den Wert 255 (0xFF), was keine gültige UTF-8 Codierung ist und außerhalb des Wertebereichs für ASCII Zeichen liegt. Das Unicode Zeichen ÿ hat denn auch den Wert U+00FF.

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11260

Wohnort: München

Dakuan schrieb:

Das Unicode Zeichen ÿ hat denn auch den Wert U+00FF.

Was dann in UTF-8 durch 2 Bytes dargestellt wird:

11000011 10111111

Dakuan

Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6500

Wohnort: Hamburg

Das ist mir bekannt, aber wegen:

Ich muss dann einen der ISO-Standards auswählen, um die Datei anzeigen / öffnen zu können.

bin ich davon ausgegangen, das dadurch eine irgendwie passende Codepage geladen wird.

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 11260

Wohnort: München

Ja, dann hat er einen Zeichensatz gewählt, der 8-Bit Zeichen nutzt, da passt das mit dem ÿ = 0xFF: ISO_8859-1

GideonRavenor

(Themenstarter)

Anmeldungsdatum:
1. März 2015

Beiträge: 171

Interessant.

Lässt UTF-8 denn nur 7-Bit-ASCII-Zeichen bzw. Zusammensetzungen daraus zu? Ich dachte es wäre der Witz am Unicode, alle Zeichen der Menschheit darstellen zu können. Was muss man denn tun, um mit C Zeichen jenseits der ASCII-Tabelle auszugeben?

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17620

Wohnort: Berlin

Nun, ich würde sagen, dass C lange vor allen UTF-Codes entworfen und standardisiert wurde.

Ich bin da nicht auf dem Laufenden, aber würde mich wundern, wenn ein char nicht nach wie vor ein Byte wäre. Und mit einem Byte kann man sicher nicht alle Zeichen der Menschheit darstellen.

Antworten |