C: Как использовать getc, чтобы избежать создания символов, отличных от UTF-8?

В настоящее время я пишу программу c, которая будет принимать 3 аргумента, два файла (один вход и один выход) и int (максимальная длина выходных строк, назовите это x). Я хочу прочитать каждую строку во входном файле и записать первые x символов в выходной файл (эффективно «обрезая» файл).

Вот мой код:

int main(int argc, char *argv[]) {

  const char endOfLine = '\n';

  if (argc < 4) {
    printf("Program takes 4 params\n");
    exit(1);
  } else {
    // Convert character argument [3] (line length) to an int
    int maxLen = atoi(argv[3]);

    char str[maxLen];
    char *inputName;
    char *outputName;

    inputName = argv[1];
    outputName = argv[2];

    // Open files to be read and written to
    FILE *inFile = fopen(inputName, "r");
    FILE *outFile = fopen(outputName, "w");

    int count = 0;
    char ch = getc(inFile);
    while (ch != EOF) {
        if (ch == '\n') {
          str[count] = (char)ch;
          printf("Adding %s to output\n", str);
          fputs(str, outFile);
          count = 0;
        } else if (count < maxLen) {
          str[count] = ch;
          printf("Adding %c to str\n", ch);
          count++;
        } else if (count == maxLen) {
          str[count] = '\n';
        }
        ch = getc(inFile);
    }

  }

  return 0;
}

Единственная проблема заключается в том, что если последний символ представляет собой одинарную кавычку, он печатает символы, отличные от UTF-8, как таковые:

For Whom t
John Donne
No man is 
Entire of 
Each is a 
A part of 
If a clod 
Europe is 
As well as
As well as
Or of thin
Each man��
For I am i
Therefore,
For whom t

person rafro4    schedule 09.12.2016    source источник
comment
У вас неопределенное поведение в разделе else if (count == maxLen), где вы переполняете свой массив.   -  person paddy    schedule 09.12.2016
comment
Что такое символ, отличный от UTF8, для потока данных, который содержит однобайтовые символы?   -  person bvj    schedule 09.12.2016
comment
@bvj Любой 8-битный char за пределами диапазона 0-127 не является правильно закодированной кодовой точкой UTF8.   -  person chux - Reinstate Monica    schedule 09.12.2016
comment
@rafro4 Каково значение int maxLen в вашем примере?   -  person chux - Reinstate Monica    schedule 09.12.2016
comment
char ch = getc(inFile); while (ch != EOF) { --› int ch; while ((count + 1 < maxLen) && (ch = getc(inFile)) != EOF) { для начала   -  person chux - Reinstate Monica    schedule 09.12.2016
comment
@chux maxLen 10 лет   -  person rafro4    schedule 09.12.2016
comment
Я изменил это, но он больше не печатает в выходной файл   -  person rafro4    schedule 09.12.2016
comment
str[count] = (char)ch; printf("Adding %s to output\n", str); происходит, когда count == 10 получает ввод "For Whom t\n", поэтому код выполняет str[10] = \n;, который записывает вне char str[maxLen];. Кроме того, нулевой символ, добавленный перед str[], не используется в printf("Adding %s to output\n", str);, поэтому printf() не знает, где заканчиваться. ИМО, здесь нет проблем с UTF8.   -  person chux - Reinstate Monica    schedule 09.12.2016
comment
@ rafro4: Есть ли причина, по которой вы не используете здесь широкий ввод и вывод?   -  person Nominal Animal    schedule 09.12.2016
comment
опубликованный код не выводит, не может, не выводит опубликованный пример вывода. Пожалуйста, опубликуйте фактический результат.   -  person user3629249    schedule 09.12.2016
comment
@user3629249 user3629249 опубликованный код работает с правильным входным файлом   -  person rafro4    schedule 09.12.2016
comment
Можно ли решить эту проблему, используя вместо этого «fgets»? Сначала я пробовал это, но не смог пропустить ни одного символа после первых 10, чтобы перейти к следующей строке (нельзя делать никаких предположений о длине строки).   -  person rafro4    schedule 09.12.2016


Ответы (1)


Вы можете проверить, был ли последний вывод char продолжающимся байтом utf-8 10xxxxxx, и если да, продолжайте вывод, пока символ не будет завершен.

// bits match 10xxxxxx
int is_utf_continue_byte(int ch){
    return ch & 0x80 && ~ch & 0x40;
}

//...
while (is_utf_continue_byte(ch))
    putchar(ch), ch = getchar();
person luser droog    schedule 09.12.2016
comment
Как мне это сделать? - person rafro4; 09.12.2016
comment
Сначала сделайте ch int, чтобы сравнение EOF было правильным, затем while (ch & 0x80 && ~ch & 0x40) putchar(ch), ch = getchar(); Это проверяет, что бит 7 равен 1 (ch & 0x80), а бит 6 равен 0 (~ch & 0x40). Для формата utf-8 этому шаблону соответствуют только непрерывные байты. - person luser droog; 09.12.2016
comment
Зачем писать это как более запутанное (ch & 0x80 && ~ch & 0x40), а не (ch & 0xC0) == 0x80? И зачем избегать фигурных скобок в циклах? - person user253751; 09.12.2016
comment
Сначала казалось концептуально проще тестировать биты по отдельности, но у вас так, как я обычно это делаю. Во-вторых, просто выбор стиля. - person luser droog; 09.12.2016