Показывать непечатаемые символы в строке

Возможно ли визуализировать непечатаемые символы в строке python с шестнадцатеричными значениями?

например. Если у меня есть строка с новой строкой внутри, я хотел бы заменить ее на \x0a.

Я знаю, что есть repr(), который даст мне... \n, но я ищу шестнадцатеричную версию.

Ответ 1

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

import re

replchars = re.compile(r'[\n\r]')
def replchars_to_hex(match):
    return r'\x{0:02x}'.format(ord(match.group()))

replchars.sub(replchars_to_hex, inputtext)

Приведенный выше пример соответствует только символам новой строки и возврату каретки, но вы можете развернуть, какие символы совпадают, в том числе с помощью \x escape-кодов и диапазонов.

>>> inputtext = 'Some example containing a newline.\nRight there.\n'
>>> replchars.sub(replchars_to_hex, inputtext)
'Some example containing a newline.\\x0aRight there.\\x0a'
>>> print(replchars.sub(replchars_to_hex, inputtext))
Some example containing a newline.\x0aRight there.\x0a

Ответ 2

Я не знаю какого-либо встроенного метода, но это довольно легко сделать с помощью понимания:

import string
printable = string.ascii_letters + string.digits + string.punctuation + ' '
def hex_escape(s):
    return ''.join(c if c in printable else r'\x{0:02x}'.format(ord(c)) for c in s)

Ответ 3

Я опаздываю на вечеринку, но если вам это нужно для простой отладки, я обнаружил, что это работает:

string = "\n\t\nHELLO\n\t\n\a\17"

procd = [c for c in string]

print(procd)

# Prints ['\n,', '\t,', '\n,', 'H,', 'E,', 'L,', 'L,', 'O,', '\n,', '\t,', '\n,', '\x07,', '\x0f,']

Ужасно, но это помогло мне найти непечатаемые символы в строке.

Ответ 4

Изменение решения ecatmur для обработки непечатаемых символов, отличных от ASCII, делает его менее тривиальным и более неприятным:

def escape(c):
    if c.printable():
        return c
    c = ord(c)
    if c <= 0xff:
        return r'\x{0:02x}'.format(c)
    elif c <= '\uffff':
        return r'\u{0:04x}'.format(c)
    else:
        return r'\U{0:08x}'.format(c)

def hex_escape(s):
    return ''.join(escape(c) for c in s)

Конечно, если str.isprintable не совсем точное определение, вы можете написать другую функцию. (Обратите внимание, что это совсем другой набор из того, что в string.printable, помимо обработки не-ASCII печатных и непечатаемых символов, он также рассматривает \n, \r, \t, \x0b и \x0c как непечатаемый.

Вы можете сделать это более компактным; это явное просто, чтобы показать все шаги, связанные с обработкой строк Unicode. Например:

def escape(c):
    if c.printable():
        return c
    elif c <= '\xff':
        return r'\x{0:02x}'.format(ord(c))
    else:
        return c.encode('unicode_escape').decode('ascii')

Действительно, независимо от того, что вы делаете, вам придется обрабатывать \r, \n и \t явно, потому что все встроенные и stdlib-функции, о которых я знаю, эти специальные последовательности вместо их шестнадцатеричных версий.

Ответ 5

Я сделал что-то подобное однажды, получив подкласс str с пользовательским методом __repr__(), который сделал то, что я хотел. Это не совсем то, что вы ищете, но можете дать вам несколько идей.

# -*- coding: iso-8859-1 -*-

# special string subclass to override the default
# representation method. main purpose is to
# prefer using double quotes and avoid hex
# representation on chars with an ord > 128
class MsgStr(str):
    def __repr__(self):
        # use double quotes unless there are more of them within the string than
        # single quotes
        if self.count("'") >= self.count('"'):
            quotechar = '"'
        else:
            quotechar = "'"

        rep = [quotechar]
        for ch in self:
            # control char?
            if ord(ch) < ord(' '):
                # remove the single quotes around the escaped representation
                rep += repr(str(ch)).strip("'")
            # embedded quote matching quotechar being used?
            elif ch == quotechar:
                rep += "\\"
                rep += ch
            # else just use others as they are
            else:
                rep += ch
        rep += quotechar

        return "".join(rep)

if __name__ == "__main__":
    s1 = '\tWürttemberg'
    s2 = MsgStr(s1)
    print "str    s1:", s1
    print "MsgStr s2:", s2
    print "--only the next two should differ--"
    print "repr(s1):", repr(s1), "# uses built-in string 'repr'"
    print "repr(s2):", repr(s2), "# uses custom MsgStr 'repr'"
    print "str(s1):", str(s1)
    print "str(s2):", str(s2)
    print "repr(str(s1)):", repr(str(s1))
    print "repr(str(s2)):", repr(str(s2))
    print "MsgStr(repr(MsgStr('\tWürttemberg'))):", MsgStr(repr(MsgStr('\tWürttemberg')))

Ответ 6

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

В приведенном ниже снимке экрана, приведенном ниже, я вставил 135-битную строку, которая имеет определенную структуру и формат (которую я должен был предварительно создать вручную для определенных позиций бит и ее общей длины), чтобы она интерпретировалась ascii конкретной программой я ' и в полученной напечатанной строке находятся непечатаемые символы, такие как "разрыв строки", который буквально вызывает разрыв строки (исправление: подача формы, новая страница, которую я имел в виду, а не разрыв строки) в в выводе на печать есть дополнительная целая пустая строка между напечатанным результатом (см. ниже):

Пример печати непечатаемых символов, которые появляются в напечатанной строке

Input a string:100100001010000000111000101000101000111011001110001000100001100010111010010101101011100001011000111011001000101001000010011101001000000
HPQGg]+\,vE!:@
>>> len('HPQGg]+\,vE!:@')
17
>>>

В приведенном выше фрагменте кода попробуйте скопировать и вставить строку HPQGg]+\,vE!:@ прямо с этого сайта и посмотреть, что произойдет, когда вы вставите ее в Python IDLE.

Подсказка: Вы должны нажать на стрелку/курсор три раза, чтобы пролистать две буквы от P до Q, даже если они появляются рядом друг с другом, так как на самом деле между ними есть команда File Separator ascii. их.

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

>>> bytes(b'HPQGg]+\,vE!:@').hex()
'48501c514767110c5d2b5c2c7645213a40'
>>> bytes.fromhex('48501c514767110c5d2b5c2c7645213a40')
b'HP\x1cQGg\x11\x0c]+\\,vE!:@'

>>> (0x48501c514767110c5d2b5c2c7645213a40 == 0b100100001010000000111000101000101000111011001110001000100001100010111010010101101011100001011000111011001000101001000010011101001000000)
True
>>> 

В приведенной выше 135-битной строке первые 16 групп по 8 бит со стороны с прямым порядком байтов кодируют каждый символ (включая непечатаемый), тогда как последняя группа из 7 бит приводит к символу @, как видно ниже:

Техническая разбивка формата вышеуказанной 135-битной строки

А здесь в качестве текста разбивка 135-битной строки:

10010000 = H (72)
10100000 = P (80)
00111000 = x1c (28 for File Separator) *
10100010 = Q (81)
10001110 = G(71)
11001110 = g (103)
00100010 = x11 (17 for Device Control 1) *
00011000 = x0c (12 for NP form feed, new page) *
10111010 = ] (93 for right bracket ‘]
01010110 = + (43 for + sign)
10111000 = \ (92 for backslash)
01011000  = , (44 for comma, ‘,)
11101100  = v (118)
10001010 = E (69)
01000010 = ! (33 for exclamation)
01110100 = : (58  for colon ‘:)
1000000  =  @ (64 for ‘@ sign)

Итак, в заключение, ответ на подвопрос об отображении непечатаемого в шестнадцатеричном виде, в байтовом массиве, который находится выше, содержит буквы x1c, которые обозначают команду разделителя файлов, которая также была отмечена в подсказке. Массив байтов можно считать строкой, если исключить префикс b с левой стороны, и снова это значение отображается в строке печати, хотя оно невидимо (хотя его присутствие можно наблюдать, как показано выше, с подсказкой и len команда).