LINUX.ORG.RU

В Си предложили добавить switch-case для строк

 , ,


0

4

Привет, чат!

Спустя много лет, в язык Си наконец предложено добавить возможность использовать выражение switch-case для строк. Например, можно будет писать вот такое:

const char *s = get_some_string();

switch(s) {
case "this":
  handle_this();
  break;
case "that":
  handle_that();
  break;
default:
  handle_other();
  break;
}

Таким образом, в Си будет чуть меньше способов отстрелить себе ноги.

Ссылка на предложение: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3895.pdf



Последнее исправление: hobbit (всего исправлений: 1)
Ответ на: комментарий от shdown

А вообще, мне wcwidth нужен для того, чтобы понять, где обрезать текст. Если оно на каком-то наркоманском вводе покажет больше, чем нужно, — это норм. Главное, что не меньше.

А этот твой libicu вообще не знает про терминоль. И как ты с помощью него это определять собрался — непонятно.

shdown ★★
()
Ответ на: комментарий от shdown

А можешь показать хоть один пример софта, кроме konsole, который воспринимает их как один символ?

У меня в системе работает с любым софтом на Qt. А с софтом на GTK не работает.

firefox, ты говоришь, тоже

Не. В поле ввода Firefox рисует один символ. При рендере готовой страницы – два. Прозреваю, потому что что шревты разные.

yorshka
() автор топика
Ответ на: комментарий от shdown

А вообще, мне wcwidth нужен для того, чтобы понять, где обрезать текст. Если оно на каком-то наркоманском вводе покажет больше, чем нужно, — это норм. Главное, что не меньше.

А этот твой libicu вообще не знает про терминоль. И как ты с помощью него это определять собрался — непонятно.

Ну ЛОЛ же ну.

yorshka
() автор топика
Ответ на: комментарий от yorshka

Ну ЛОЛ же ну.

Ну и чего мне твой анализ рубежей? Не, ты покажи, как ты собираешься с libicu посчитать эту хрень для терминоля.

Хорошо, установил я твой konsole. Он сдвигает курсор на два знакоместа. Как мне это посчитать с помощью libicu?

#include <unicode/ubrk.h>
#include <unicode/utypes.h>
#include <unicode/ustring.h>
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>

int count_grapheme_clusters(const UChar *text, int32_t length) {
    UErrorCode status = U_ZERO_ERROR;
    int count = 0;

    // Create a character break iterator (grapheme cluster boundaries)
    UBreakIterator *bi = ubrk_open(UBRK_CHARACTER, "en_US", text, length, &status);
    if (U_FAILURE(status)) {
        fprintf(stderr, "Error opening break iterator: %s\n", u_errorName(status));
        return -1;
    }

    int32_t boundary = ubrk_first(bi);
    while (boundary != UBRK_DONE) {
        int32_t next = ubrk_next(bi);
        if (next == UBRK_DONE) break;
        count++;
        boundary = next;
    }

    ubrk_close(bi);
    return count;
}

int main() {
    enum { N = 2048 };

    UChar text[N];
    UErrorCode status = U_ZERO_ERROR;

    size_t nline = 0;
    char *line = NULL;
    if (getline(&line, &nline, stdin) <= 0) {
        fprintf(stderr, "getline() failed.\n");
        return 1;
    }

    if (strlen(line) && line[strlen(line) - 1] == '\n') {
        line[strlen(line) - 1] = '\0';
    }

    int32_t destLength;
    u_strFromUTF8(text, N, &destLength, line, -1, &status);

    if (U_FAILURE(status)) {
        fprintf(stderr, "Conversion error: %s\n", u_errorName(status));
        return 1;
    }

    int graphemes = count_grapheme_clusters(text, destLength);
    printf("Grapheme clusters: %d\n", graphemes);
    printf("UTF-16 code units: %d\n", destLength);

    return 0;
}

Она говорит, что 1 grapheme cluster.

shdown ★★
()
Последнее исправление: shdown (всего исправлений: 1)
Ответ на: комментарий от shdown

Ну и чего мне твой анализ рубежей?

Вот для этого он нужен, балда:

А вообще, мне wcwidth нужен для того, чтобы понять, где обрезать текст.

Он сдвигает курсор на два знакоместа. Как мне это посчитать с помощью libicu?

Теперь ты понимаешь, что юникод – это тебе не в тапки срать? Повторю ещё раз: количество знакомест зависит от графемы и шрифта. Мой пример выше – два эмоджи, склеенных в один через zero-width joiner. Если шрифт поддерживает эту комбинацию, будет одно знакоместо. Если не поддерживает, будет два.

Чтобы подсчитать всё это, тебе нужна библиотека Harfbuzz, потому что – как я писал выше – шейпер из libicu выкинули. И, да, там уже нет никаких знакомест, там уже пиксели высчитываются. См. примерно вот сюда.

yorshka
() автор топика
Ответ на: комментарий от yorshka

Не. В поле ввода Firefox рисует один символ. При рендере готовой страницы – два. Прозреваю, потому что что шревты разные.

У меня с большой задержкой рисует. Сначала два показывает, потом через секунд десть уже один.

Dr64h ★★★★
()

Не самая плохая идея!

vbr ★★★★★
()
Ответ на: комментарий от Dr64h

Прикол, теперь не могу повторить. При перезагрузке страницы всё наоборот, рисуется сначала один чубрик и через секунду «рука-лицо и стрелка», хотя при первой загрузке, сначала были две кракозябры, потом чубрик.

Dr64h ★★★★
()
Ответ на: комментарий от Dr64h

Опять же, ставлю на подгрузку шревтов и твой тормозной комп. Но, в любом случае, тут суть в том, что символов не пять, как это говорит хвалёная функция wcswidth.

К слову

If a nonprintable wide character occurs among these characters, -1 is returned.

Оно должно вообще на ZWJ тут выругаться, ибо

The zero-width joiner (ZWJ) is a non-printing character used in the computerized typesetting of some complex scripts such as the Arabic script or any Indic script.

Либо же я чего-то не понимаю в этой документации. Как бы то ни было, поведение wcswidth в некорректно с любой стороны.

yorshka
() автор топика
Ответ на: комментарий от yorshka

как без <wchar.h> понять, сколько знакомест в терминале займёт такая-то строка

Чтобы подсчитать всё это, тебе нужна библиотека Harfbuzz

И, да, там уже нет никаких знакомест

Да ты же поехавший! Терминал оперирует знакоместами, а не пикселями. Можно запросить, на каком столбце сейчас курсор (целое число). А для пикселей вот ANSI escape code нет.

shdown ★★
()
Ответ на: комментарий от shdown

Терминал оперирует знакоместами, а не пикселями.

Нет. Раньше оперировал, во времена телетайпов и обязательных моноширинных шрифтов. Как только мы добавляем эмоджи, лигатуры и прочие юникодные радости, все это едет в лес.

Ситуация достаточно шизофреническая выходит, да. Мы притворяемся, что у нас терминал с квадратно-гнездовым размещением знаков, а потом суём в него эмоджи с какающим беременным мужчиной (который на самом деле три эмоджи и два ZWJ), от чего тот сходит с ума.

Можно запросить, на каком столбце сейчас курсор (целое число).

Запросить ты можешь, но ответ, как ты уже смог увидеть, тебе не понравится.

А для пикселей вот ANSI escape code нет.

Но твой терминал рисует юникод, а не ANSI. Речь об этом.

yorshka
() автор топика
Ответ на: комментарий от yorshka

обязательных моноширинных шрифтов

Не, ну в порядке извращения ты можешь заставить терминал использовать не-моноширинный шрифт, но он всё равно будет рисовать символы в сетке с постоянной шириной.

Мы притворяемся, что у нас терминал с квадратно-гнездовым размещением знаков, а потом суём в него эмоджи с какающим беременным мужчиной (который на самом деле три эмоджи и два ZWJ), от чего тот сходит с ума.

Нет, просто есть символы, которые занимают одно знакоместо, и есть те, которые занимают два (а есть те, которые занимают ноль). Ещё до популярности юникода была half-width katakana и full-width katakana. А кандзи всегда два знакоместа занимают. А есть ещё английские буквы, которые занимают два знакоместа! TRY ENTERING THIS.

Короче, общая концепция вполне sound, просто не все терминоли соглашаются между собой, какие code points сколько знакомест занимают. Есть proposal для стандартизации этой фигни.

Запросить ты можешь, но ответ, как ты уже смог увидеть, тебе не понравится.

Почему не понравится? Он что, врёт?

> А для пикселей вот ANSI escape code нет.

Но твой терминал рисует юникод, а не ANSI. Речь об этом.

Ты перепутал ANSI с ASCII?

shdown ★★
()
Последнее исправление: shdown (всего исправлений: 4)
Ответ на: комментарий от shdown

Не, ну в порядке извращения ты можешь заставить терминал использовать не-моноширинный шрифт, но он всё равно будет ПЫТАТЬСЯ рисовать символы в сетке с постоянной шириной.

Поправил. Не факт, что всегда будет получаться успешно.

Короче, общая концепция вполне sound, просто не все терминоли соглашаются между собой, какие code points сколько знакомест занимают. Есть proposal для стандартизации этой фигни.

Я не про концепцию здесь, а про реальное состояние вещей. Идея, конечно, вполне адекватно, кто бы спорил-то.

Запросить ты можешь, но ответ, как ты уже смог увидеть, тебе не понравится.

Почему не понравится? Он что, врёт?

А ты не заметил? Здесь правда пять символов?

$ cat emoji 
🤦🏼‍♂
$ ./foo < emoji 
5

Ты перепутал ANSI с ASCII?

Нет.

yorshka
() автор топика
Ответ на: комментарий от yorshka

> > > Можно запросить, на каком столбце сейчас курсор (целое число).

> > Запросить ты можешь, но ответ, как ты уже смог увидеть, тебе не понравится.

> Почему не понравится? Он что, врёт?

А ты не заметил? Здесь правда пять символов?

wcswidth не запрашивает позицию курсора у терминала. Когда у терминала запрашивают позицию курсора, он не врёт.

Ты забываешь контекст?

shdown ★★
()
Ответ на: комментарий от shdown

wcswidth не запрашивает позицию курсора у терминала. Когда у терминала запрашивают позицию курсора, он не врёт.

Тогда верно. Я думал, ты хотел посчитать позицию через длину выведенного с начала строки.

С другой стороны, я вполне видел глюки, когда курсор мигал в середине последнего выведенного символа, потому что он оказался длиннее. Как раз из-за причин выше.

yorshka
() автор топика
Ответ на: комментарий от yorshka

я не знаком с растом и изучать пока не готов, но считаю его допустимым языком для молодого поколения программистов

sni4ok
()
Ответ на: комментарий от yorshka

На самом деле количество знакомест надо считать как количество utf-8 последовательностей (ну, тех которые парсятся в числа от 0 до 10FFFF). И рисовать их соответственно на фиксированных координатах. Всякую юникодную чушь про много-кодпоинтовые символы не обращать внимания при реализации, от неё только проблемы могут возникнуть. И то что помечено как zero-width тоже рисовать одним знакоместом (можно рисовать как пробел, как знак вопроса, как квадратик, или ещё каким заполнителем).

firkax ★★★★★
()
Ответ на: комментарий от firkax

Всякую юникодную чушь про много-кодпоинтовые символы не обращать внимания при реализации, от неё только проблемы могут возникнуть.

Это сразу сломает все языки с умляутами и другими сложными символами. За что ты ненавидишь немцев?

И то что помечено как zero-width тоже рисовать одним знакоместом (можно рисовать как пробел, как знак вопроса, как квадратик, или ещё каким заполнителем).

Совсем упоролся, что ли?

yorshka
() автор топика
Ответ на: комментарий от yorshka

Это сразу сломает все языки с умляутами и другими сложными символами. За что ты ненавидишь немцев?

Без юникода всё это работало в виде одного байта а теперь одного кодпоинта не хватает? Если не считать всякую экзотику то всё будет работать по идее. Если кому нужны типографские изыски - для них есть гуи-редакторы, а в консоли нужно понятное и простое расположение символов в первую очередь.

Совсем упоролся, что ли?

Нет, все индексы кодировочной таблицы должны себя вести строго одинаково в геометрическом аспекте. Отличия должны быть только в содержании пикселей прямоугольника, выделенного для отображения знака.

firkax ★★★★★
()
Ответ на: комментарий от firkax

Это сразу сломает все языки с умляутами и другими сложными символами. За что ты ненавидишь немцев?Без юникода всё это работало в виде одного байта а теперь одного кодпоинта не хватает?

Это не работало. В одной кодировке ты не мог смешивать два языка помимо английского (ASCII).

Если не считать всякую экзотику то всё будет работать по идее.

Не будет. Кто-нибудь выведет какой-нибудь арабский или иврит и у тебя всё поедет.

а в консоли нужно понятное и простое расположение символов в первую очередь.

Консоли бывают разные. Если у тебя консоль – это RS-232 на железку, где кроме ASCII ничего нет, это одна консоль. Если у тебя терминал на десктопе, где ты эмоджи выводишь, это другая консоль. Кстати, я люблю эмоджи в терминале, например, как индикатор приоритета в логах.

Нет, все индексы кодировочной таблицы должны себя вести строго одинаково в геометрическом аспекте.

Это не про юникод. Сорян, но добро пожаловать в реальную жизнь.

yorshka
() автор топика
Ответ на: комментарий от yorshka

Это не работало. В одной кодировке ты не мог смешивать два языка помимо английского (ASCII).

И что? Немецкая кодировка то имелась, и все символы из неё под какими-то индексами есть и в юникоде. Безо всяких двойных кодпоинтов.

Не будет. Кто-нибудь выведет какой-нибудь арабский или иврит и у тебя всё поедет.

Поедет не у меня а у того кто вывел.

Консоли бывают разные. Если у тебя консоль – это RS-232 на железку, где кроме ASCII ничего нет, это одна консоль. Если у тебя терминал на десктопе, где ты эмоджи выводишь, это другая консоль. Кстати, я люблю эмоджи в терминале, например, как индикатор приоритета в логах.

Консоль - это прямоугольная сетка из знакомест, каждой (x,y) паре координат которой соответствует ровно один символ (включая возможный пробел - пустоту). Все отклонения от данного правила - вредительство и/или идиотизм и должны быть пресечены.

Это не про юникод.

Да и плевать. Будет стандарт «garbage-free unicode».

firkax ★★★★★
()
Ответ на: комментарий от firkax

Поедет не у меня а у того кто вывел.

Т.е. ты антисемит? Ну-ну…

Да и плевать. Будет стандарт «garbage-free unicode».

А зачем? Потому что сишникам сложно юникод реализовать? Так он уже реализован для них, можно просто использовать.

yorshka
() автор топика
Ответ на: комментарий от yorshka

А зачем?

Потому что консоль должна быть матрицей из чётко различимых знакомест, а не невнятной кашей. Для символов произвольной ширины/высоты и не пойми каких координат есть гуи. Си (и вообще языки программирования) тут ни при чём.

firkax ★★★★★
()
Ответ на: комментарий от Stanson

что значит нету отличий? отличие есть: удобство и производительность на современном железе. чтобы делать chop_right() через вычитание, а не циклом. Разницу между циклом и вычитанием объяснять, в ассемблере?

BruteForce ★★★★
()
Последнее исправление: BruteForce (всего исправлений: 1)
Ответ на: комментарий от firkax

Кандзи тупо более сложны геометрически, чем латиница, поэтому их ещё с древних времён (поищи в любимом поисковике «half-width katakana») рисуют в два знакоместа: https://i.postimg.cc/d389HLQh/2026-08-08-165453-103x44-scrot.png

Про RTL уже сказали.

shdown ★★
()
Ответ на: комментарий от BruteForce

чтобы делать chop_right() через вычитание, а не циклом.

Цикл-то всё равно никуда не делся. Ты просто вынес его за скобки. :) Что std::string, что std::string_view один хрен должны откуда-то узнать длину строки в буфере из которого их инициализировали. :)

сишечная строка точно так же может быть без малейших проблем обрезана вычитанием, если до этого тебе уже пришлось где-то посчитать её длину чтобы прочитать, скопировать, или ещё что. Строки не берутся из воздуха. Либо их длина известна в compile-time, либо один хрен на том или ином этапе получения вычисляется. Отличие только в том, что в сишечке тебе совершенно не обязательно отслеживать и постоянно хранить где-то длину строки если тебе это не требуется. В цепепе, при использовании std:string и пр., ты от этих накладных расходов отказаться уже не сможешь.

И самое забавное - обрезание строки с конца обычно требуется не на какую-то фиксированнную длину, а по местоположению какого-нибудь символа. Например чтобы получить директорию в которой находится файл из полного пути, или там отрезать дробную часть. А как раз в этом случае std::string никак и ничем помочь не может. Строки он не индексирует по символам (а если бы индексировал, то был бы жуткой блоатварью), так что одна чорт придётся искать нужный символ во всей строке, прежде чем её обрезать с конца, как и в случае с ASCIIZ.

Stanson ★★★★★
()
Ответ на: комментарий от firkax

Консоль - это прямоугольная сетка из знакомест, каждой (x,y) паре координат которой соответствует ровно один символ (включая возможный пробел - пустоту). Все отклонения от данного правила - вредительство и/или идиотизм и должны быть пресечены.

Бедные японцы, китайцы и корейцы с их символами двойной ширины. Наверное, их всех следовало утилизировать, дабы firkax’у стройную картину не портили…

CrX ★★★★★
()
Ответ на: комментарий от Stanson

Сравнивать будем asciiz строки и что-то более современное. В чем твой поинт, что работа бесполезная выполняется? У неё должна быть цена. Память лишняя естся? Ее посчитать должно, и оценить влияние. Неудобны слайсы? Я могу сказать, что struct {char*ptr; int size;} мне удобнее. А ты пробовал?

BruteForce ★★★★
()
Ответ на: комментарий от shdown

Если их рисуют в два знакоместа, то это должны быть два символа - «левая половина такого-то иероглифа» и «правая половина такого-то иероглифа».

firkax ★★★★★
()
Ответ на: комментарий от BruteForce

Сравнивать будем asciiz строки и что-то более современное.

И на входе во всех случаях будет ASCIIZ строка, да?

Я могу сказать, что struct {char*ptr; int size;} мне удобнее. А ты пробовал?

А мне удобнее когда строка в самой мякотке компилятора что-то типа [size][codepage][length][text]['\0'] и при этом *(str+n) или str[n] это n-ный символ, а всякие malloc,free,alloca и пр. работают с str как с куском памяти. Разумеется при этом нет никаких структур, классов или ещё каких-либо лишних сущностей. Просто базовый тип данных компилятора «строка», точно такой же базовый и неделимый как float или double с их мантисами, знаками экспонентами и пр.

Stanson ★★★★★
()
Ответ на: комментарий от Siborgium

Символ - это элемент таблицы знакогенератора, то есть сущность, расположенная под заданным индексом, и представляющая из себя битмап фиксированного (x,y)-размера. Что в нём нарисовано - несущественно. Никто не запрещает тебе сделать символы «левая половина клоуна» и «правая половина клоуна» и, ставя их рядом, получать клоуна двойной ширины. Но технически это будут два символа - у каждого будет свой индекс и свои координаты.

firkax ★★★★★
()
Ответ на: комментарий от CrX

Что за чушь? Просто у них знакоместа не прямоугольные а квадратные нужны. Кстати, среди стандартных текстовых PC-видеорежимов такие есть, 8х8, правда большинство иероглифов, подозреваю, в 8х8 нарисовать затруднительно будет. Ну, им нужны либо особые режимы с 16х16 символами, либо гуи-эмуляция. Но 16х16 должны быть все символы, а не только иероглифы, на китайско-совместимых консолях.

На твоё вероятное замечание про некрасивую уширенную 16х16 латиницу/кириллицу сразу отвечу: хочешь типографские изыски - используй гуи-верстальные форматы и софт, там тебе и буквы любой ширины и ещё много всего. А консоль всем этим портить не надо, она должна быть геометрически консистентной.

firkax ★★★★★
()
Ответ на: комментарий от Stanson

Это не сишечная строка, уж давай звать это «способ передачи строки либц» хотя бы. В языке си строкового типа нет, а к поиниерам на чар + asciiz никто не принуждает.

И при чем тут стд-стринг?

И разве не хорошо не вызывать стрлен там, где можно еще в компайлтайме узнать длину?

BruteForce ★★★★
()
Последнее исправление: BruteForce (всего исправлений: 1)
Ответ на: комментарий от BruteForce

Это не сишечная строка, уж давай звать это «способ передачи строки либц» хотя бы.

Это тип данных «строка» который теоретически мог бы быть лучше ASCIIZ. А не вот эти все убогие костыли типа std::string или колхозных структурок.

Stanson ★★★★★
()
Ответ на: комментарий от Stanson

И на входе во всех случаях будет ASCIIZ строка, да?

Или нет.

А мне удобнее

Тогда и спорить не о чем

BruteForce ★★★★
()
Ответ на: комментарий от Stanson

Не понимаю. Комитет подумол, родил стд-стринг. Тебе не нра. Комитет ещо подумол, родил стд-стринг-вю и стд-спан. Тебе тоже не нра. Я недумол и наколхозил структурку — ты всё еще недоволен. Оставить как есть askiiz? Да не трогает их никто, вон в свитчи пихают, будь доволен.

BruteForce ★★★★
()
Ответ на: комментарий от BruteForce

Вообще непонятно, чем char*+int лучше, чем char*+size_t, потому что на 64-битном всё равно padding будет у int и получившаяся структура будет 16 байт, а не 12. На 32-битном и так, и так будет 8 байт.

А тут ещё ssize_t какой-то, который как бы по стандарту может в себя вмещать только -1 и значения >=0.

shdown ★★
()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.