【问题标题】:How to change multicharacter signs by other ones in C?如何用C中的其他字符更改多字符符号?
【发布时间】:2016-12-21 14:50:31
【问题描述】:

我有一个 UTF-8 文本文件,其中包含几个我想被其他人更改的标志(仅限于 |( 和 |) 之间的那些),但问题是有些这些符号中的一些不被视为字符,而是被视为多字符符号。 (我的意思是它们不能放在“∞”之间,而只能放在“∞”之间,所以 char * ?)

这是我的文本文件:

Text : |(abc∞∪v=|)

例如:

应改为 ¤c

by ¸!

= 更改为 "

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

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

fgetc 不能使用,因为 ∞ 之类的字符不能被视为单个字符。如果我使用它,我将无法使用每个符号(char *)strcmp 一个字符,我试图转换我的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++;
    }
}

非常感谢您以后的帮助!

编辑:如果我在每个符号之间放置一个空格,每个符号都会正确更改,但如果没有,它将不起作用,这就是我要解决的问题。

【问题讨论】:

标签: c char


【解决方案1】:

首先,正确使用术语。正确的术语有点令人困惑,但至少其他人会理解你在说什么。

在 C 中,charbyte 相同。然而,character 是抽象的东西,例如 ¤c。一个字符可能包含几个字节(即几个chars)。这种字符称为多字节字符。

将字符转换为字节序列(编码)并非易事。不同的系统做的不同;一些使用UTF-8,而另一些可能使用UTF-16 big-endian、UTF-16 little endian、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
    }
}

【讨论】:

  • 这真的帮助解决了我的问题!我刚刚更改了您代码中的一些内容
【解决方案2】:

您在正确的轨道上,但您需要以不同于字符串的方式看待字符。

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

(假装i = 2)如您所知,这会将字符串"∞"&amp;a[2] 进行比较。但是您忘记了 &amp;a[2] 是字符串的第二个字符的地址,而 strcmp 的工作原理是扫描整个字符串,直到遇到空终止符。所以 "∞" 实际上最终会与 "abc∞∪v=|)" 进行比较,因为 a 只是在最后以 null 结尾。

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

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

那个 8734 的原因是因为那是 UTF16 value of infinity

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

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

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

这意味着您永远不会真正进行迭代。您需要返回并重新考虑您的扫描程序。

【讨论】:

  • 假设 UTF16 是文本文件上使用的编码:) OP 没有指定。
  • @AhmedMasud 好点。当我写这个问题时,我了解到,不猜测就不可能得到文本文件的编码。我的知识有限,但是不使用一些猜测库,OP可能会处于真正的泡菜中。
  • 我的文本文件是用 UTF-8 编写的!
猜你喜欢
  • 2016-06-19
  • 2013-03-07
  • 1970-01-01
  • 2020-12-12
  • 2022-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-21
相关资源
最近更新 更多