PureBasic - форум

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » PureBasic - форум » Вопросы по PureBasic » UTF8 to ASCII


UTF8 to ASCII

Сообщений 1 страница 30 из 57

1

Помогите решить такую задачу:

На входе - текстовый файл с русско-английским текстом в кодировке UTF-8,
в которм всречаются символы, выходящие за рамки ASCII.
На выходе - файл с тем же текстом в кодировке ASCII, в котором символы,
не имеющиеся в ASCII, отображались бы в виде HEX-кода между "&#x" и ";" (например: ¾ и т.п.).

Т.е. ровно так, как делает WriteString(#File, Text$, #PB_Ascii), только вместо знака вопроса в местах
не Ascii символов их hex-код.

0

2

Я не понял смысл этого действа. Ну сохрани в UTF-8 и это будет ASCII с HEX-кодами из UTF-8

Andruk написал(а):

например: &#xBE

Ну вообщее у UTF-8 два байта на символ, а &#xBE это один байт.

0

3

Пробовал по-простому - шибко медленно...

Код:
If ReadFile(0, "1.txt", #PB_UTF8)
  CreateFile(1, "2.txt", #PB_Ascii)
  While Not Eof(0)
    s$ = ReadString(0)
    rs$ = ""
    k=Len(s$)
    For i = 1 To k
      c = Asc(Mid(s$, i, 1))
      If c<188 Or (c>1024 And c<1106) Or c=8211 Or c=8230
        rs$ = rs$ + Chr(c)
      Else
        rs$ = rs$ + "&#x" + Hex(c) + ";"
      EndIf
    Next
     WriteStringN(1, rs$)
  Wend
  CloseFile(0)
  CloseFile(1)
  MessageRequester("Инфо","Готово!")
Else
    MessageRequester("Инфо","Не удалось открыть файл!")
EndIf

Отредактировано Andruk (05.11.2025 18:34:40)

0

4

Andruk написал(а):

Я не понял смысл этого действа

Согласен - специфическая хотелка.

Ну сохрани в UTF-8 и это будет ASCII с HEX-кодами из UTF-8

Теперь я не понял...

В общем, хочу получить из текста в кодировке UTF-8 читаемый текст (notepad и др.) в кодировке windows-1251.

Отредактировано Andruk (05.11.2025 18:46:23)

0

5

Собственно, цель состоит в том, чтобы получить файл меньшего размера с сохранением изредка встречающихся
символов, отсутствующих в кодировке windows-1251.

Если этот текст оформить как html, xml то он будет отображаться корректно в соответствующих приложениях.

Отредактировано Andruk (05.11.2025 23:53:45)

0

6

выйдет Html меньшего размера.

0

7

Andruk написал(а):

Собственно, цель состоит в том, чтобы получить файл меньшего размера с сохранением изредка встречающихся символов, отсутствующих в кодировке windows-1251.

Тогда сохраняйте в UTF-8 с BOM.
В этом формате используется переменное число байт для хранения символов.

UTF-8 (Unicode Transformation Format, 8-bit) — это кодировка, которая преобразует символы из стандарта Unicode в компьютерные данные. Она является наиболее популярной и универсальной кодировкой в интернете, позволяющей отображать любые символы, включая латиницу, кириллицу и эмодзи. Её ключевые особенности – переменная длина (1–4 байта на символ) и обратная совместимость с ASCII, благодаря чему файлы, содержащие только ASCII-символы, остаются идентичными в обоих форматах.

0

8

Andruk написал(а):

Собственно, цель состоит в том, чтобы получить файл меньшего размера

велика ли экономия?
или это там где мало памяти?

Пётр написал(а):

переменная длина (1–4 байта на символ

а как отличать кому сколько байт надо?

Отредактировано newJS (06.11.2025 14:29:37)

0

9

Так уже побыстрее

Код:
If ReadFile(0, "1.fb2", #PB_UTF8)
  CreateFile(1, "2.fb2", #PB_Ascii)
  WriteStringN(1, ReplaceString(ReadString(0), "UTF-8", "windows-1251")) ;Только для fb2
  While Not Eof(0)
    st$ = ReadString(0)
    *Ascii = Ascii(st$)
    st2$ = PeekS(*Ascii, -1, #PB_Ascii)
    FreeMemory(*Ascii)
    nb = CountString(st$ , "?")
    nb2 = CountString(st2$ , "?")
    If nb2 And nb2 > nb
      st3$=st2$ : t=0
      p = FindString(st2$, "?", 1)
      While p
        p2=FindString(st$, "?", p)
        If p<>p2
          cd$="&#x" + Hex(Asc(Mid(st$, p, 1))) + ";"
          st3$ = ReplaceString(st3$, "?", cd$, 0, p+t, 1)
          t+Len(cd$)-1
        EndIf
        p = FindString(st2$, "?", p+1)
      Wend
      WriteStringN(1, st3$)
    Else
      WriteStringN(1, st2$)
    EndIf  
  Wend
  CloseFile(0)
  CloseFile(1)
  MessageRequester("Инфо","Готово!")
Else
  MessageRequester("Инфо","Не удалось открыть файл!")
EndIf

Отредактировано Andruk (06.11.2025 20:35:50)

0

10

Andruk
В теории вы пытаетесь получить UTF8, потому что он именно так и выглядит, у него Ascii символы представлены одним байтом, а UTF8 символы представлены 2 и более байтами. UTF8 это минимально возможная комбинация текста без потери данных. Единственная проблема если в вашем fb2 символы представлены как псевдокод, типа &#xBE, то есть на один байт у вас 5 символов, то есть 5 байтов.

0

11

newJS написал(а):

велика ли экономия?

Тестовый файл в UTF-8 - 1 145 101 байт, он же в windows-1251 - 652 979 байт.

newJS написал(а):

или это там где мало памяти?

Сугубо частный случай. У меня есть планшет с невеликой памятью (слота под карту памяти нет).
Использую в том числе для чтения книг, коих у меня много-много (никогда не знаешь, что захочется почитать).
И вот я изголяюсь, дабы вместить на этот планшет побольше книг.
Купить другой планшет не предлагайте - из доходов токма пенсия (не депутатская), да и привыкли глаза к нему.

0

12

AZJIO написал(а):

В теории вы пытаетесь получить UTF8...

Почему такая разница в размерах? Непонятно...

0

13

Andruk
а файл можешь дать?

0

14

Поправил код - не во всех случаях работал правильно.

Код:
If ReadFile(0, "1.fb2", #PB_UTF8)
  CreateFile(1, "2.fb2", #PB_Ascii)
  WriteStringN(1, ReplaceString(ReadString(0), "UTF-8", "windows-1251")) ;Только для fb2 (xml)
  While Not Eof(0)
    st$ = ReadString(0)
    *Ascii = Ascii(st$)
    st2$ = PeekS(*Ascii, -1, #PB_Ascii)
    FreeMemory(*Ascii)
    nb = CountString(st$ , "?")
    nb2 = CountString(st2$ , "?")
    If nb2 And nb2 > nb
      st3$=st2$ : t=0
      p = FindString(st2$, "?", 1)
      While p
        p2=FindString(st$, "?", p)
        If p<>p2
          cd$="&#x" + Hex(Asc(Mid(st$, p, 1))) + ";"
          st3$ = ReplaceString(st3$, "?", cd$, 0, p+t, 1)
          t+Len(cd$)-1
        EndIf
        p = FindString(st2$, "?", p+1)
      Wend
      WriteStringN(1, st3$)
    Else
      WriteStringN(1, st2$)
    EndIf  
  Wend
  CloseFile(0)
  CloseFile(1)
  MessageRequester("Инфо","Готово!")
Else
  MessageRequester("Инфо","Не удалось открыть файл!")
EndIf

Отредактировано Andruk (06.11.2025 21:29:16)

0

15

AZJIO написал(а):

а файл можешь дать?

Ну так любой текстовый в utf-8 на русском и английском подойдет, необязательно с особыми символами.

0

16

Добавил строчку только для файлов fb2 (xml), чтобы читалка не путалась.
Если не fb2 (xml), закомментируйте.

Отредактировано Andruk (06.11.2025 21:28:44)

0

17

AZJIO
fb2

0

18

Andruk
С англ. не подойдёт, так как они в обоих кодировках будут однобайтовыми. А вот русский да, будет двух-файтовый, а в 1251 однобайтовый. Думаю надо сделать регвыр который выявит все буквы кроме диапазона a-zA-ZА-яЁё или даже вместо a-zA-Z указать весь диапазон от 1 до 127 т.е. \x{01}-\x{7F} как то так

Код:
[^А-яЁё\x{01}-\x{7F}]

Добавь символы исключения взяв тут

Код:
; 	исключаем символы, так как они являются сами собой и не требуют подмены
	DeleteMapElement(cp1251(), Chr(152))
	DeleteMapElement(cp1251(), Chr(160))
	DeleteMapElement(cp1251(), Chr(164))
	DeleteMapElement(cp1251(), Chr(166))
	DeleteMapElement(cp1251(), Chr(167))
	DeleteMapElement(cp1251(), Chr(169))
	DeleteMapElement(cp1251(), Chr(171))
	DeleteMapElement(cp1251(), Chr(172))
	DeleteMapElement(cp1251(), Chr(173))
	DeleteMapElement(cp1251(), Chr(176))
	DeleteMapElement(cp1251(), Chr(177))
	DeleteMapElement(cp1251(), Chr(181))
	DeleteMapElement(cp1251(), Chr(182))
	DeleteMapElement(cp1251(), Chr(183))
	DeleteMapElement(cp1251(), Chr(187))

В общем задача получить все символы кроме указанных в массив, а лучше сразу в карту, так убираем дубликаты. Потом сделать замену каждого такого символа на себя с шеснадцатеричным кодом, что-то вроде &#x + Hex(Asc(символ))

Вот новый регвыр

Код:
[^А-яЁё\x{01}-\x{7F}ЂЃ‚ѓ„…†‡€‰Љ‹ЊЌЋЏђ‘’“”•–—™љ›њќћџ ЎўЈ¤Ґ¦§©Є«¬­®Ї°±Ііґµ¶·№є»јЅѕї]

Получил его в AutoIt3 используя код

Код:
$sText = ""
For $i = 128 To 256
    $sText &= Chr($i)
Next
MsgBox(4096, "Символы между 128 To 256", $sText)

В AutoIt3 есть ChrW() и Chr(), последняя использует диапазон Win1251, т.е. локальную кодировку языка ОС.

Итак первая версия кода

Код:
;- TOP
EnableExplicit

;- # Constants
#RegExp = 0

;- ● Define
Define Text$
Define SourcePath$ = "C:\Users\user\Downloads\1.fb2"
Define DestinationPath$ = "C:\Users\user\Downloads\2.fb2"


Procedure.s ReadFileToVarUTF8(Path$)
	Protected id_file, Format, Text$

	id_file = ReadFile(#PB_Any, Path$, #PB_UTF8)
	If id_file
    Text$ = ReadString(id_file, #PB_UTF8 | #PB_File_IgnoreEOL)
    CloseFile(id_file)
	EndIf

	ProcedureReturn Text$
EndProcedure


Procedure.s XML_UTF8_To_Win1251(Text$)
	Protected symbolcode, symbol$
	Protected pattern$ = "[^А-яЁё\x{01}-\x{7F}ЂЃ‚ѓ„…†‡€‰Љ‹ЊЌЋЏђ‘’“”•–—™љ›њќћџ ЎўЈ¤Ґ¦§©Є«¬­®Ї°±Ііґµ¶·№є»јЅѕї]"
	Protected NewMap SymbolMap.i()

	If CreateRegularExpression(#RegExp, "[^А-яЁё\x{01}-\x{7F}ЂЃ‚ѓ„…†‡€‰Љ‹ЊЌЋЏђ‘’“”•–—™љ›њќћџ ЎўЈ¤Ґ¦§©Є«¬­®Ї°±Ііґµ¶·№є»јЅѕї]", 0)
    If ExamineRegularExpression(#RegExp, Text$)
    	While NextRegularExpressionMatch(#RegExp)
        If Not FindMapElement(SymbolMap() , RegularExpressionMatchString(#RegExp))
        	AddMapElement(SymbolMap(), RegularExpressionMatchString(#RegExp), #PB_Map_NoElementCheck)
        EndIf
        SymbolMap() + 1
    	Wend
    EndIf
; 	Else
;     Debug RegularExpressionError()
	EndIf
	
	ForEach SymbolMap()
    ; 	Debug MapKey(SymbolMap())
    ; 	Debug SymbolMap()
    symbol$ = MapKey(SymbolMap())
    symbolcode = Asc(symbol$)
    ; 	Можно похожие символы заменить на их обычные аналоги, например разного типа пробелы, тире и т.д.
    Select symbolcode
    	Case $2010 ; тире
        Text$ = ReplaceString(Text$, symbol$, "-", 1, SymbolMap())
    	Case $2002 ; пробел широкий как два
        Text$ = ReplaceString(Text$, symbol$, "  ", 1, SymbolMap())
    	Case $2003 ; пробел широкий как три
        Text$ = ReplaceString(Text$, symbol$, "   ", 1, SymbolMap())
    	Case $2004 To $200B, $202F, $205F ; пробел
        Text$ = ReplaceString(Text$, symbol$, " ", 1, SymbolMap())
    	Default ; стандартная замена для любого символа
        Text$ = ReplaceString(Text$, symbol$, "&#x" + Hex(symbolcode) + ";", 1, SymbolMap())
    EndSelect
	Next
	
	; Кодировку в заголовке XML тоже меняем
	Text$ = ReplaceString(Text$, ~"encoding=\"UTF-8\"?>", ~"encoding=\"windows-1251\"?>")

	ProcedureReturn Text$
EndProcedure


Procedure.s SaveFileToAscii(Path$, Text$)
	Protected id_file
	id_file = CreateFile(#PB_Any, Path$)
	If id_file
    WriteString(id_file, Text$, #PB_Ascii)
    CloseFile(id_file)
	EndIf
EndProcedure

Text$ = ReadFileToVarUTF8(SourcePath$)
Text$ = XML_UTF8_To_Win1251(Text$)
SaveFileToAscii(DestinationPath$, Text$)

Отредактировано AZJIO (07.11.2025 10:55:54)

0

19

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

Andruk написал(а):

У меня есть планшет с невеликой памятью (слота под карту памяти нет).

чёрт, я приколося, а оно и правда памяти мало, бывает
а архивировать если, возни конечно больше

0

20

AZJIO написал(а):

Итак первая версия кода

Спасибо. Замысловато.
Все-таки остановлюсь на своем последнем варианте. Он прост, достаточно быстр и делает то,
что мне надо (на данный момент) со всем набором utf-8 символов.

0

21

newJS написал(а):

а архивировать если, возни конечно больше

Так я и архивирую, и меняю кодировку, и уменьшаю размер обложки, и проверяю на валидность и т.д. и т.п.
Только делаю все это посредством трех-четырех прог.
Вот подумал свести эти действа воедино в одном приложении. С кодировкой, картинками и пр. - все понятно.
А вот с валидацией пока неясно. Парсер в pure не работает с схемами xsd (dtd) - потому пока не подступался.

0

22

Andruk
А зачем спрашивать если всё работает? Так, чтобы погонять народ?

Мой код умеет сейчас подменять UTF8 пробелы на обычные или UTF8 тире на обычное, и можно дальше наращивать там где Case.

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

Отредактировано AZJIO (07.11.2025 15:09:33)

0

23

AZJIO написал(а):

А зачем спрашивать если всё работает? Так, чтобы погонять народ?

Так когда спрашивал еще не работало - не сразу дошло, как можно сделать и не предполагал, что дойдет.
Поспешил, наверное.
А ваш код я сохранил, т. к. он вполне вероятно мне пригодится. Спасибо, что откликнулись.

Чтобы ускорить, надо читать файл бинарно (ReadData)... ....обработать сотни файлов, вместо поштучно

Да, так и буду делать. Просто мне надо было найти механизм такой перекодировки.

Отредактировано Andruk (07.11.2025 16:57:56)

0

24

Andruk
Если прям уж нужно вылизать код, оптимизировать его идеально, то и ваш и мой есть куда стремиться. В моём можно возвращать позиции найденных, чтобы заменять без поиска по позициям в оригинале. Аналогично и в вашем можно записывать по позициям без FindString() и CountString(), двигаться посимвольно. Как я понял у вас всё основано на том, что конвертация UTF8 в Ascii даёт "?" для широких символов. Сама идея нормальная, только наверно она идеально быстро сработает посимвольным сдвигом, без прыжков по позициям, без Mid().

0

25

AZJIO
Да, реализация не очень...(это я про свой код).
Насчет идеи. Если срАвниваемые строки равны по кол-ву вопросительных знаков (а таких много больше), то они вообще не рассматриваются.
А в некоторых книгах других может и не быть вовсе. Поэтому Mid() в данном случае особой роли не играет. (Про посимвольный сдвиг хотелось бы узнать побольше).
Можно загрузить исходный файл в память полностью, но текущая реализация предусматривает построчную запись.
Надо переделывать.

Отредактировано Andruk (07.11.2025 19:39:49)

0

26

Andruk написал(а):

Про посимвольный сдвиг хотелось бы узнать побольше

вот

0

27

Сегодня 19:48:07
AZJIO
Ага! Это есть в переведенном вами хелпе.
Когда-то глянул мельком и забыл. Поэкспериментирую на досуге.
Спасибо.

0

28

Малость оптимизировал:

Код:
If ReadFile(0, "1.fb2", #PB_UTF8)
  CreateFile(1, "2.fb2", #PB_Ascii)
  WriteStringN(1, ReplaceString(ReadString(0), "UTF-8", "windows-1251"))
  While Not Eof(0)
    st$ = ReadString(0)
    *Ascii = Ascii(st$)
    st2$ = PeekS(*Ascii, -1, #PB_Ascii)
    FreeMemory(*Ascii)
    If CountString(st2$, "?")-CountString(st$, "?")
      t=0 : p = FindString(st2$, "?", 1)
      While p
        cd$ = Mid(st$, p-t, 1)
        If cd$<>"?"  
          cd$="&#x"+Hex(Asc(cd$))+";"
          st2$ = ReplaceString(st2$, "?", cd$, 0, p, 1)
          t+Len(cd$)-1
        EndIf
        p = FindString(st2$, "?", p+1)
      Wend
    EndIf
    WriteStringN(1, st2$)
  Wend
  CloseFile(0) : CloseFile(1)
  MessageRequester("Инфо","Готово!")
Else
  MessageRequester("Инфо","Не удалось открыть файл!")
EndIf

Отредактировано Andruk (10.11.2025 17:19:42)

0

29

Как я не пытался это сделать без построчного чтения-записи из файла в файл,
нифига быстрее последнего варианта не получается...

И делаю такой вывод: при построчном анализе текста построчное чтение и построчная запись
намного быстрее.

Или я ошибаюсь?

Отредактировано Andruk (10.11.2025 23:18:03)

0

30

Не-е, не ошибаюсь.

0


Вы здесь » PureBasic - форум » Вопросы по PureBasic » UTF8 to ASCII