我有一个 UTF-8文本文件,其中包含几个我希望被其他符号更改的标志(只有|(和|)之间的那些),但问题是有些这些标志不被视为字符,而是多字符标志。 (我的意思是他们不能放在'∞'之间,但只有这个“∞”,所以char *?)
这是我的文本文件:
Text : |(abc∞∪v=|)
例如:
∞应由¤c
更改 ¸!∪
= 由“
更改因为有些符号(∞和∪)是多字符,我决定使用fscanf逐字逐句地获取所有文本。这个方法的问题是我必须在每个字符之间放置空格......我的文件应该如下所示:
Text : |( a b c ∞ ∪ v = |)
fgetc无法使用,因为像∞这样的字符不能被视为单个字符。如果我使用它,我将无法用每个符号(char *)strcmp一个字符,我试图转换我的char to char *但strcmp!= 0。
这是我在C中的代码,可以帮助您理解我的问题:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(void){
char *carac[]={"∞","=","∪"}; //array with our signs
FILE *flot,*flot3;
flot=fopen("fichierdeTest2.txt","r"); // input text file
flot3=fopen("resultat.txt","w"); //output file
int i=0,j=0;
char a[1024]; //array that will contain each read word.
while(!feof(flot))
{
fscanf(flot,"%s",&a[i]);
if (strstr(&a[i], "|(") != NULL){ // if the word read contains |( then j=1
j=1;
fprintf(flot3,"|(");
}
if (strcmp(&a[i], "|)") == 0)
j=0;
if(j==1) { //it means we are between |( and |) so the conversion can begin
if (strcmp(carac[0], &a[i]) == 0) { fprintf(flot3, "¤c"); }
else if (strcmp(carac[1], &a[i]) == 0) { fprintf(flot3,"\"" ); }
else if (strcmp(carac[2], &a[i]) == 0) { fprintf(flot3, " ¸!"); }
else fprintf(flot3,"%s",&a[i]); // when it's a letter, number or sign that doesn't need to be converted
}
else { // when we are not between |( and |) just copy the word to the output file with a space after it
fprintf(flot3, "%s", &a[i]);
fprintf(flot3, " ");
}
i++;
}
}
非常感谢未来的帮助!
编辑:如果我在每个符号之间放置一个空格但是没有,它将无法正常工作,那么每个符号都会正确更改。这就是我想要解决的问题。
答案 0 :(得分:4)
首先,请使用正确的术语。适当的术语有点令人困惑,但至少其他人会理解你在说什么。
在C中,char
与 byte 相同。但是,字符是抽象的,例如∞
或¤
或c
。一个字符可能包含几个字节(即几个char
s)。这些字符称为多字节字符。
将字符转换为字节序列(编码)并非易事。不同的系统做得与众不同;有些人使用UTF-8,有些则可能使用UTF-16大端,UTF-16小端,8位codepage或任何其他编码。
当你的C程序有引号内的内容时,如"∞"
- 它是一个C字符串,即几个字节以零字节终止。当您的代码使用strcmp
比较字符串时,它会比较两个字符串的每个字节,以确保它们相等。因此,如果您的源代码和输入文件使用不同的编码,则字符串(字节序列)不会匹配,即使您在检查它们时会看到相同的字符!
因此,要排除任何编码不匹配,您可能希望在源代码中使用字节序列而不是字符。例如,如果您知道输入文件使用UTF-8编码:
char *carac[]={
"\xe2\x88\x9e", // ∞
"=",
"\xe2\x88\xaa"}; // ∪
或者,确保编码(源代码和程序的输入文件)相同。
另一个不那么微妙的问题:在比较字符串时,你实际上有一个大字符串和一个小字符串,你想要检查大字符串是否以小字符串开头。这里strcmp
做错了!您必须在此处使用strncmp
:
if (strncmp(carac[0], &a[i], strlen(carac[0])) == 0)
{
fprintf(flot3, "\xC2\xA4""c"); // ¤c
}
另一个问题(实际上是一个主要错误):fscanf
函数从输入文件中读取单词(由空格分隔的文本)。如果只检查该字中的第一个字节,则不会处理其他字节。要修复,请对所有字节进行循环:
fscanf(flot,"%s",a);
for (i = 0; a[i] != '\0'; )
{
if (strncmp(&a[i], "|(", 2)) // start pattern
{
now_replacing = 1;
i += 2;
continue;
}
if (now_replacing)
{
if (strncmp(&a[i], whatever, strlen(whatever)))
{
fprintf(...);
i += strlen(whatever);
}
}
else
{
fputc(a[i], output);
i += 1; // processed just one char
}
}
答案 1 :(得分:1)
你走在正确的轨道上,但你需要以不同于字符串的方式来看字符。
strcmp(carac[0], &a[i])
(假装i = 2
)如您所知,这会将字符串"∞"
与&a[2]
进行比较。但是你忘记了&a[2]
是字符串第二个字符的地址,strcmp通过扫描整个字符串直到它遇到空终止符来工作。因此,"∞"
实际上最终会与"abc∞∪v=|)"
进行比较,因为a
仅在最后终止。
你应该做的不是使用字符串,而是将每个字符(8位)扩展为短(16位)。然后你可以将它们与你的UTF-16字符进行比较
if( 8734 = *((short *)&a[i])) { /* character is infinity */ }
8734的原因是因为那是UTF16 value of infinity。
非常重要注意事项: 取决于您的机器是big-endian还是little-endian对于这种情况。如果8734(0x221E)不起作用,请尝试7714(0x1E22)。
编辑我忽略的其他事情是你一次扫描整个字符串。 “%s:字符串。这将读取后续字符,直到找到空格(空格字符被视为空白,换行符和制表符)。” (source)
//feof = false.
fscanf(flot,"%s",&a[i]);
//feof = ture.
这意味着你永远不会实际迭代。您需要返回并重新考虑扫描程序。