我在从字符串中删除非utf8字符时出现问题,这些字符无法正确显示。像这样的字符0x97 0x61 0x6C 0x6F(十六进制表示)
删除它们的最佳方法是什么?正则表达式还是其他?
我在从字符串中删除非utf8字符时出现问题,这些字符无法正确显示。像这样的字符0x97 0x61 0x6C 0x6F(十六进制表示)
删除它们的最佳方法是什么?正则表达式还是其他?
Answers:
使用正则表达式方法:
$regex = <<<'END'
/
(
(?: [\x00-\x7F] # single-byte sequences 0xxxxxxx
| [\xC0-\xDF][\x80-\xBF] # double-byte sequences 110xxxxx 10xxxxxx
| [\xE0-\xEF][\x80-\xBF]{2} # triple-byte sequences 1110xxxx 10xxxxxx * 2
| [\xF0-\xF7][\x80-\xBF]{3} # quadruple-byte sequence 11110xxx 10xxxxxx * 3
){1,100} # ...one or more times
)
| . # anything else
/x
END;
preg_replace($regex, '$1', $text);
它搜索UTF-8序列,并将其捕获到组1中。它还匹配无法识别为UTF-8序列的一部分的单个字节,但不捕获这些字节。替换是捕获到组1中的任何内容。这将有效删除所有无效字节。
通过将无效字节编码为UTF-8字符,可以修复字符串。但是,如果错误是随机的,则可能会留下一些奇怪的符号。
$regex = <<<'END'
/
(
(?: [\x00-\x7F] # single-byte sequences 0xxxxxxx
| [\xC0-\xDF][\x80-\xBF] # double-byte sequences 110xxxxx 10xxxxxx
| [\xE0-\xEF][\x80-\xBF]{2} # triple-byte sequences 1110xxxx 10xxxxxx * 2
| [\xF0-\xF7][\x80-\xBF]{3} # quadruple-byte sequence 11110xxx 10xxxxxx * 3
){1,100} # ...one or more times
)
| ( [\x80-\xBF] ) # invalid byte in range 10000000 - 10111111
| ( [\xC0-\xFF] ) # invalid byte in range 11000000 - 11111111
/x
END;
function utf8replacer($captures) {
if ($captures[1] != "") {
// Valid byte sequence. Return unmodified.
return $captures[1];
}
elseif ($captures[2] != "") {
// Invalid byte of the form 10xxxxxx.
// Encode as 11000010 10xxxxxx.
return "\xC2".$captures[2];
}
else {
// Invalid byte of the form 11xxxxxx.
// Encode as 11000011 10xxxxxx.
return "\xC3".chr(ord($captures[3])-64);
}
}
preg_replace_callback($regex, "utf8replacer", $text);
编辑:
!empty(x)
将匹配非空值("0"
被视为空)。x != ""
将匹配非空值,包括"0"
。x !== ""
将匹配除以外的任何内容""
。x != ""
在这种情况下,似乎是最好的选择。
我也加快了比赛速度。而不是单独匹配每个字符,它匹配有效的UTF-8字符序列。
$regex = <<<'END'
PHP <5.3.x 使用什么代替?
elseif (!empty($captures([2])) {
您应该使用!== ""
空而不是空,因为它"0"
被认为是空的。而且此功能非常慢,可以更快吗?
如果您utf8_encode()
已将字符串应用于UTF8,它将返回乱码的UTF8输出。
我做了一个解决所有这些问题的功能。叫做Encoding::toUTF8()
。
您不需要知道字符串的编码是什么。它可以是Latin1(ISO8859-1),Windows-1252或UTF8,或者字符串可以混合使用。Encoding::toUTF8()
会将所有内容转换为UTF8。
我之所以这样做,是因为某项服务使我的数据馈送全乱了,将这些编码混合在同一字符串中。
用法:
require_once('Encoding.php');
use \ForceUTF8\Encoding; // It's namespaced now.
$utf8_string = Encoding::toUTF8($mixed_string);
$latin1_string = Encoding::toLatin1($mixed_string);
我包括了另一个函数Encoding :: fixUTF8(),该函数将修复每个UTF8字符串,这些字符串看起来已经多次编码为UTF8,因此看起来是乱码。
用法:
require_once('Encoding.php');
use \ForceUTF8\Encoding; // It's namespaced now.
$utf8_string = Encoding::fixUTF8($garbled_utf8_string);
例子:
echo Encoding::fixUTF8("Fédération Camerounaise de Football");
echo Encoding::fixUTF8("Fédération Camerounaise de Football");
echo Encoding::fixUTF8("FÃÂédÃÂération Camerounaise de Football");
echo Encoding::fixUTF8("Fédération Camerounaise de Football");
将输出:
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football
下载:
您可以使用mbstring:
$text = mb_convert_encoding($text, 'UTF-8', 'UTF-8');
...将删除无效字符。
<0x1a>
<0x1a>
,虽然不是可打印字符,但它是完全有效的UTF-8序列。您可能遇到不可打印字符的问题?检查:stackoverflow.com/questions/1176904/...
ini_set('mbstring.substitute_character', 'none');
否则结果中将出现问号。
此函数删除所有NON ASCII字符,这很有用,但不能解决问题:
这是我的函数,无论编码如何,它始终有效:
function remove_bs($Str) {
$StrArr = str_split($Str); $NewStr = '';
foreach ($StrArr as $Char) {
$CharNo = ord($Char);
if ($CharNo == 163) { $NewStr .= $Char; continue; } // keep £
if ($CharNo > 31 && $CharNo < 127) {
$NewStr .= $Char;
}
}
return $NewStr;
}
这个怎么运作:
echo remove_bs('Hello õhowå åare youÆ?'); // Hello how are you?
í
地址字段中的字符,它是有效的UTF-8字符,请参见表。士气:不要相信API错误消息:)
$text = iconv("UTF-8", "UTF-8//IGNORE", $text);
这就是我正在使用的。似乎工作得很好。取自http://planetozh.com/blog/2005/01/remove-invalid-characters-in-utf-8/
试试这个:
$string = iconv("UTF-8","UTF-8//IGNORE",$string);
根据iconv手册,该函数将第一个参数作为输入字符集,第二个参数作为输出字符集,第三个参数作为实际输入字符串。
如果将输入和输出字符集都设置为UTF-8,并将//IGNORE
标志附加到输出字符集,则该函数将丢弃(剥离)输入字符串中所有不能由输出字符集表示的字符。因此,过滤输入字符串有效。
iconv
。@halfer也许您的输入数据不是来自utf-8。另一个选择是重新转换为ascii,然后再次返回到utf-8。在我来说,我没有使用iconv
像$output = iconv("UTF-8//", "ISO-8859-1//IGNORE", $input );
文本可能包含非utf8字符。尝试先做:
$nonutf8 = mb_convert_encoding($nonutf8 , 'UTF-8', 'UTF-8');
您可以在此处了解更多信息:http : //php.net/manual/en/function.mb-convert-encoding.php 新闻
从PHP 5.5开始可以使用UConverter。如果使用国际扩展名而不使用mbstring,则UConverter是更好的选择。
function replace_invalid_byte_sequence($str)
{
return UConverter::transcode($str, 'UTF-8', 'UTF-8');
}
function replace_invalid_byte_sequence2($str)
{
return (new UConverter('UTF-8', 'UTF-8'))->convert($str);
}
自PHP 5.4起,htmlspecialchars可用于删除无效的字节序列。Htmlspecialchars在处理大字节大小和准确性方面优于preg_match。可以看到很多使用正则表达式的错误实现。
function replace_invalid_byte_sequence3($str)
{
return htmlspecialchars_decode(htmlspecialchars($str, ENT_SUBSTITUTE, 'UTF-8'));
}
我做了一个从字符串中删除无效的UTF-8字符的函数。我用它来清除27000产品的描述,然后再生成XML导出文件。
public function stripInvalidXml($value) {
$ret = "";
$current;
if (empty($value)) {
return $ret;
}
$length = strlen($value);
for ($i=0; $i < $length; $i++) {
$current = ord($value{$i});
if (($current == 0x9) || ($current == 0xA) || ($current == 0xD) || (($current >= 0x20) && ($current <= 0xD7FF)) || (($current >= 0xE000) && ($current <= 0xFFFD)) || (($current >= 0x10000) && ($current <= 0x10FFFF))) {
$ret .= chr($current);
}
else {
$ret .= "";
}
}
return $ret;
}
ord()
返回结果,范围为0-255。if
此函数中的巨人ord()
将测试永不返回的unicode范围。如果有人想弄清楚为什么该功能以它的工作方式起作用,我将不胜感激。
欢迎来到2019和/u
regex中的修饰符,它将为您处理UTF-8多字节字符
如果仅使用mb_convert_encoding($value, 'UTF-8', 'UTF-8')
,字符串中仍然会出现不可打印的字符
该方法将:
mb_convert_encoding
\r
,\x00
(NULL字节)和其他控制字符preg_replace
function utf8_filter(string $value): string{
return preg_replace('/[^[:print:]\n]/u', '', mb_convert_encoding($value, 'UTF-8', 'UTF-8'));
}
[:print:]
匹配所有可打印的字符和\n
换行符,并剥离其他所有内容
您可以在下面看到ASCII表。可打印字符的范围是32到127,但是换行符\n
是控制字符的一部分,范围是0到31,因此我们必须在正则表达式中添加换行符/[^[:print:]\n]/u
您可以尝试通过正则表达式发送字符串,其中字符超出可打印范围,例如 \x7F
(DEL),\x1B
(Esc)等,并查看如何剥离它们
function utf8_filter(string $value): string{
return preg_replace('/[^[:print:]\n]/u', '', mb_convert_encoding($value, 'UTF-8', 'UTF-8'));
}
$arr = [
'Danish chars' => 'Hello from Denmark with æøå',
'Non-printable chars' => "\x7FHello with invalid chars\r \x00"
];
foreach($arr as $k => $v){
echo "$k:\n---------\n";
$len = strlen($v);
echo "$v\n(".$len.")\n";
$strip = utf8_decode(utf8_filter(utf8_encode($v)));
$strip_len = strlen($strip);
echo $strip."\n(".$strip_len.")\n\n";
echo "Chars removed: ".($len - $strip_len)."\n\n\n";
}
php-mbstring
默认情况下未在php中打包。
$string = preg_replace('~&([a-z]{1,2})(acute|cedil|circ|grave|lig|orn|ring|slash|th|tilde|uml);~i', '$1', htmlentities($string, ENT_COMPAT, 'UTF-8'));
从最近的补丁到Drupal的Feed JSON解析器模块:
//remove everything except valid letters (from any language)
$raw = preg_replace('/(?:\\\\u[\pL\p{Zs}])+/', '', $raw);
如果您担心,它将保留空格作为有效字符。
做了我所需要的。它删除了当今不流行的表情符号字符,这些字符不适合MySQL的'utf8'字符集,并且给了我类似“ SQLSTATE [HY000]:常规错误:1366不正确的字符串值”的错误。
有关详细信息,请参见https://www.drupal.org/node/1824506#comment-6881382
iconv
远比基于老式的正则表达式更好preg_replace
,至极是时下弃用。
ereg_replace()
,抱歉。
也许不是最精确的解决方案,但是它只需一行代码即可完成工作:
echo str_replace("?","",(utf8_decode($str)));
utf8_decode
将字符转换为问号;
str_replace
将删除问号。
因此规则是,第一个UTF-8八位位组将高位设置为标记,然后设置1到4位以指示还有多少个八位位组;那么每个附加八位字节都必须将高两位设置为10。
伪python将是:
newstring = ''
cont = 0
for each ch in string:
if cont:
if (ch >> 6) != 2: # high 2 bits are 10
# do whatever, e.g. skip it, or skip whole point, or?
else:
# acceptable continuation of multi-octlet char
newstring += ch
cont -= 1
else:
if (ch >> 7): # high bit set?
c = (ch << 1) # strip the high bit marker
while (c & 1): # while the high bit indicates another octlet
c <<= 1
cont += 1
if cont > 4:
# more than 4 octels not allowed; cope with error
if !cont:
# illegal, do something sensible
newstring += ch # or whatever
if cont:
# last utf-8 was not terminated, cope
同样的逻辑应该可以翻译成php。但是,不清楚在获得格式错误的字符后将执行哪种剥离。
c = (ch << 1)
将(c & 1)
在第一次使零,跳过循环。测试可能应该是(c & 128)
与问题略有不同,但是我正在做的是使用HtmlEncode(string),
伪代码在这里
var encoded = HtmlEncode(string);
encoded = Regex.Replace(encoded, "&#\d+?;", "");
var result = HtmlDecode(encoded);
输入和输出
"Headlight\x007E Bracket, { Cafe Racer<> Style, Stainless Steel 中文呢?"
"Headlight~ Bracket, { Cafe Racer<> Style, Stainless Steel 中文呢?"
我知道这并不完美,但可以为我完成工作。
static $preg = <<<'END'
%(
[\x09\x0A\x0D\x20-\x7E]
| [\xC2-\xDF][\x80-\xBF]
| \xE0[\xA0-\xBF][\x80-\xBF]
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}
| \xED[\x80-\x9F][\x80-\xBF]
| \xF0[\x90-\xBF][\x80-\xBF]{2}
| [\xF1-\xF3][\x80-\xBF]{3}
| \xF4[\x80-\x8F][\x80-\xBF]{2}
)%xs
END;
if (preg_match_all($preg, $string, $match)) {
$string = implode('', $match[0]);
} else {
$string = '';
}
它对我们的服务有效