如何在C语言中用其他字符替换多个字符标志?

4
我有一个UTF-8文本文件,其中包含几个符号,我想把它们改成其他符号(只限于在|(和|)之间的符号),但问题是有些符号不被视为字符,而是被视为多字符符号。(我的意思是它们不能放在'∞'中间,只能像这样“∞”,那么char *?)
这是我的文本文件:
Text : |(abc∞∪v=|)

例如:

应该改为 ¤c

改为 ¸!

= 改为 "

因此,由于一些符号(如∞和∪)是多个字符,我决定使用fscanf逐字获取所有文本。这种方法的问题在于我必须在每个字符之间加上空格...我的文件应该像这样:

Text : |( a b c ∞ ∪ v = |)

"fgetc不能使用,因为像∞这样的字符不能被视为单个字符。如果我使用它,我将无法使用strcmp将char与每个符号(char *)进行比较,我试图将我的char转换为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++;
    }
}

非常感谢未来的帮助!
编辑:如果我在每个符号之间加一个空格,那么每个符号都会被正确更改,但是没有空格,它就不起作用,这是我正在尝试解决的问题。

fgetwc() 呢? - ad absurdum
好的问题格式,但有一些小问题:避免使用 feof(https://dev59.com/jG035IYBdhLWcg3wbPU5),将 j 改成其他不同的名称,比如 is_converting 或其他什么,因为 j 通常是一个迭代器。 - Dellowar
考虑使用fread()而不是fscanf()。由于您正在使用带有多字节字符的UTF-8,因此您需要有一种机制来读取字节流,然后逐个处理字符并识别UTF-8流中的多字节字符。另请参见UTF8 processing C,以及这篇博客文章Using UTF-8 as the internal representation for strings in C and C++ with Visual Studio - Richard Chambers
C: 使用 scanf 和 wchar_t 读取和打印 UTF-8 字符串 是一个简短的演示程序,展示了如何使用 setlocale(LC_ALL, ""); 以及 %ls 格式说明符,例如 scanf("%ls",string); - Richard Chambers
2个回答

4
首先,正确使用术语。适当的术语有点令人困惑,但至少其他人能够理解你在说什么。
在C中,charbyte相同。但是,一个字符是一些抽象的内容,如¤c。一个字符可能包含几个字节(即几个char)。这样的字符称为多字节字符。
将字符转换为一系列字节(编码)并不容易。不同的系统采用不同的方法;有些使用UTF-8,而其他一些可能使用UTF-16 big-endian、UTF-16 little endian、8位代码页或任何其他编码
当你的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
    }
}

那真的帮助我解决了问题!我只是在你的代码中做了一些更改。 - BinX

1

你走在正确的道路上,但需要将字符与字符串区分开来。

strcmp(carac[0], &a[i])

(Pretending i = 2) 正如你所知,这将比较字符串"∞"&a[2]。但你忘记了&a[2]是字符串第二个字符的地址,并且strcmp通过扫描整个字符串直到遇到空终止符来工作。因此"∞"实际上最终会与"abc∞∪v=|)"进行比较,因为a仅在最后一个位置处以空终止符结束。

你应该做的是不使用字符串,而是将每个字符(8位)扩展为short(16位)。然后你可以将它们与你的UTF-16字符进行比较。

if( 8734 = *((short *)&a[i])) { /* character is infinity */ }

这里的8734是因为它是无穷大的UTF16值

非常重要的提示: 这种情况取决于您的机器是大端还是小端。如果8734(0x221E)不起作用,请尝试使用7714(0x1E22)。

编辑 我忽略的另一件事是您正在一次扫描整个字符串。"%s:字符的字符串。这将读取后续字符,直到找到空格(空格字符被视为空白、换行和制表符)"(来源)。

//feof = false.
fscanf(flot,"%s",&a[i]); 
//feof = ture.

这意味着你实际上从未进行迭代。你需要回过头来重新思考你的扫描流程。

假设UTF16是文本文件使用的编码方式 :) OP没有指定。 - Ahmed Masud
1
@AhmedMasud 很好的观点。在我写这个问题的时候,我了解到没有猜测就无法获得文本文件的编码。我的知识有限,但是如果没有使用一些猜测库,OP可能会陷入困境。 - Dellowar
我的文本文件是用UTF-8编写的! - BinX

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接