C#移除字符串中的不可见Unicode字符 案例代码
作者:程序设计实验室 时间:2023-04-28 19:06:06
C#移除字符串中的不可见Unicode字符
背景
最近发现某个数据采集的系统拿下来的数据,有些字段的JSON被莫名截断了,导致后续数据分析的时候解析JSON失败。
类似这样
{"title": "你好
或者这样,多了个双引号啥的
{"title":""你好"}
因为数据库是Oracle,起初以为是Oracle这老古董出问题了,结果一番折腾,把每条写入数据的SQL语句都拿出来,看起来里面的JSON格式都没问题。
这也太诡异了吧,看起来没毛病,但就为啥JSON被随机截断呢?
最后我试着把整段SQL放在Rider的 query console 里面执行,然后再去数据库里读取这段JSON,居然发现变成这样了:
{"title":"?你好"}
啊这,看到这个大大的问号,立刻就能知道这个“你好”里面不止是这两个字,肯定含有不可见的Unicode字符。
然后把这段JSON复制出来,用16进制模式打开,果然看到在“你好”前面有一个 \u0020
的字符…
Unicode码表
0000-007F:C0控制符及基本拉丁文 (C0 Control and Basic Latin)
0080-00FF:C1控制符及拉丁文补充-1 (C1 Control and Latin 1 Supplement)
0100-017F:拉丁文扩展-A (Latin Extended-A)
0180-024F:拉丁文扩展-B (Latin Extended-B)
0250-02AF:国际音标扩展 (IPA Extensions)
02B0-02FF:空白修饰字母 (Spacing Modifiers)
……
这里再附上部分 Unicode 表格
U+ | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0000 | NUL | SOH | STX | ETX | EOT | ENQ | ACK | BEL | BS | HT | LF | VT | FF | CR | SO | SI |
0010 | DLE | DC1 | DC2 | DC3 | DC4 | NAK | SYN | ETB | CAN | EM | SUB | ESC | FS | GS | RS | US |
0020 | ! | " | # | $ | % | & | ' | ( | ) | * | + | , | - | . | / | |
0030 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | : | ; | < | = | > | ? |
0040 | @ | A | B | C | D | E | F | G | H | I | J | K | L | M | N | O |
0050 | P | Q | R | S | T | U | V | W | X | Y | Z | [ | \ | ] | ^ | _ |
0060 | ` | a | b | c | d | e | f | g |
可以看到上面那个 \u0020
在第三行第一列,是一个不可见字符,躲在标题的前面
也就是因为这个 Unicode 字符,Oracle无法正确解析,所以导致了插入数据的时候错乱了
所以破案了,就是系统前台使用人员,在输入的时候不知道咋滴搞了个Unicode字符进去…
解决方法就是我这边采集的时候再做一次过滤…
没想到C#要搞个过滤 Unicode 还挺折腾的,资料太少…
最后还是参考了Java的资料搞的。= =...
代码
代码如下
写了个扩展方法来过滤
public static class StringExt {
// 控制字符
private static readonly Regex ControlCharRegex = new Regex(@"[\p{C}]", RegexOptions.Compiled);
/// <summary>
/// 移除控制字符
/// </summary>
public static string RemoveControlChars(this string text) {
return ControlCharRegex.Replace(text, string.Empty);
}
}
要使用的时候就这样
var outStr = "带有Unicode的字符串".RemoveControlChars();
搞定。
参考资料
UniCode编码表及部分不可见字符过滤方案 - https://www.cnblogs.com/fan-yuan/p/8176886.html
https://stackoverflow.com/questions/6198986/how-can-i-replace-non-printable-unicode-characters-in-java
补充:C# 字符串与unicode互相转换实战案例
代码如下所示:
/// <summary>
/// 字符串转Unicode
/// </summary>
/// <param name="source">源字符串</param>
/// <returns>Unicode编码后的字符串</returns>
public static string String2Unicode(string source)
{
var bytes = Encoding.Unicode.GetBytes(source);
var stringBuilder = new StringBuilder();
for (var i = 0; i < bytes.Length; i += 2)
{
stringBuilder.AppendFormat("\\u{0:x2}{1:x2}", bytes[i + 1], bytes[i]);
}
return stringBuilder.ToString();
}
/// <summary>
/// 字符串转为UniCode码字符串
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static string StringToUnicode(string s)
{
char[] charbuffers = s.ToCharArray();
byte[] buffer;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < charbuffers.Length; i++)
{
buffer = System.Text.Encoding.Unicode.GetBytes(charbuffers[i].ToString());
sb.Append(String.Format("\\u{0:X2}{1:X2}", buffer[1], buffer[0]));
}
return sb.ToString();
}
/// <summary>
/// Unicode字符串转为正常字符串
/// </summary>
/// <param name="srcText"></param>
/// <returns></returns>
public static string UnicodeToString(string srcText)
{
string dst = "";
string src = srcText;
int len = srcText.Length / 6;
for (int i = 0; i <= len - 1; i++)
{
string str = "";
str = src.Substring(0, 6).Substring(2);
src = src.Substring(6);
byte[] bytes = new byte[2];
bytes[1] = byte.Parse(int.Parse(str.Substring(0, 2), System.Globalization.NumberStyles.HexNumber).ToString());
bytes[0] = byte.Parse(int.Parse(str.Substring(2, 2), System.Globalization.NumberStyles.HexNumber).ToString());
dst += Encoding.Unicode.GetString(bytes);
}
return dst;
}
来源:https://www.cnblogs.com/deali/p/17085516.html
![](/images/zang.png)
![](/images/jiucuo.png)
猜你喜欢
Java中Stream流中map和forEach的区别详解
![](https://img.aspxhome.com/file/2023/8/108428_0s.jpg)
你可知HashMap为什么是线程不安全的
IntelliJ IDEA 2017.1.4 x64配置步骤(介绍)
![](https://img.aspxhome.com/file/2023/5/71885_0s.jpg)
带你用C语言实现strtok和字符串分割函数
![](https://img.aspxhome.com/file/2023/0/125920_0s.png)
RocketMQ-延迟消息的处理流程介绍
![](https://img.aspxhome.com/file/2023/3/62713_0s.png)
利用Java如何获取Mybatis动态生成的sql接口实现
Android 弹出Dialog时隐藏状态栏和底部导航栏的方法
Jetpack Compose 的新型架构 MVI使用详解
![](https://img.aspxhome.com/file/2023/2/139392_0s.jpg)
Java Swing中的JButton、JComboBox、JList和JColorChooser组件使用案例
![](https://img.aspxhome.com/file/2023/1/77051_0s.jpg)
利用Java Set 去除重复object的方法
spring boot整合RabbitMQ实例详解(Fanout模式)
![](https://img.aspxhome.com/file/2023/0/108390_0s.png)
MyBatis自定义映射关系和关联查询实现方法详解
![](https://img.aspxhome.com/file/2023/2/60902_0s.png)
JPA Specification常用查询+排序实例
![](https://img.aspxhome.com/file/2023/3/59423_0s.png)
详解Android TextView属性ellipsize多行失效的解决思路
Java实现配置加载机制
SpringCloud Zuul过滤器和谷歌Gauva实现限流
![](https://img.aspxhome.com/file/2023/5/125225_0s.png)
C#中实现Json序列化与反序列化的几种方式
![](https://img.aspxhome.com/file/2023/5/71065_0s.png)
C#事件(event)使用方法详解
Android开发获取重力加速度和磁场强度的方法
![](https://img.aspxhome.com/file/2023/2/106022_0s.png)
SpringBoot @Import与@Conditional注解使用详解
![](https://img.aspxhome.com/file/2023/5/106095_0s.png)