在要由 C# 读取的 .txt 文件中包含控制字符
Including control characters in a .txt file to be read by C#
我正在开发一个使用纯 ASCII .txt 文件作为 key/value 配置文件的项目。 ConfigFile.txt 的当前格式类似于
名字=埃尔默|姓氏=Fudd|用户 ID=EFudd|密码=fubar|日期=2016 年 7 月 29 日
这很容易读入程序并使用 KeyValuePairs 创建字典,代码如下:
using (FileStream fs = new FileStream("ConfigFile.txt", FileMode.Open))
{
using (StreamReader sr = new StreamReader(fs))
{
string fileText = sr.ReadToEnd();
// Tokenize the entire file string into separate key=value strings.
string[] tokens = fileText.Split('|');
// Iterate through all of the key=value strings, tokenize each one into a key=Value
// pair and add the key and value as separate strings into the dictionary.
foreach (string token in tokens)
{
string[] keyValuePair = token.Split('=');
configDict.Add(keyValuePair[0], keyValuePair[1]);
}
}
}
它首先使用'|'将每个key/value拆分为一个单独的字符串作为分隔符。
名字=埃尔默
姓氏=Fudd
UserId=EFudd
密码=foobar
日期=2016 年 7 月 29 日
然后,对于每个 key/value 字符串,它在 '=' 分隔符上分隔键和值,创建一个 KeyValuePair,并将其插入到字典中供以后在程序中查找。
到目前为止一切顺利。指示用户不要使用任何一个分隔符创建密码。但是,我现在必须在将密码包含在文件中之前对其进行加密,并且加密例程可以生成从 0x20 到 0x7F 的任何可打印字符。因此,加密密码可以以其中一个或两个分隔符结尾。我最终可以得到 'foobar'(或其他)被加密引擎加密成 P#|=g%。这扰乱了 split 函数正常工作的能力。
所以,我想更改输入到记事本 .txt 文件中的分隔符来控制字符,而不是“|”分隔符,我正在使用 0x1E(记录分隔符)并将“=”符号替换为 0x1F(单元分隔符)。
我可以直接在 C# 中进行转义和编码,没有任何问题,但是我如何修改原始的 .txt 磁盘文件,以便它能够正确读取分隔符作为单个(不可打印的)字符?
因此,我不会使用那样的纯文本,而是使用适当的序列化格式,例如 JSON。
有一些工具可以为您完成艰苦的工作。
内置的 System.Web.Script.Serialization
命名空间有一些工具可以使用,但我更喜欢使用 Json.Net。如果您有 Visual Studio,您可以使用 nuGet 安装它(如果您需要更多帮助,请在评论中告诉我)。
但是一旦你将它添加到你的项目中,你就可以做这样的事情
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
namespace ConsoleApplication1
{
public class Program
{
static void Main(string[] args)
{
var dict = new Dictionary<string, string>();
dict.Add("FirstName", "Elmer");
dict.Add("LastName", "Fudd");
dict.Add("Password", @"\a\ansld\sb\b8d95nj");
var json = JsonConvert.SerializeObject(dict);
File.WriteAllText("ConfigFile.txt, json);
var txt = File.ReadAllText("ConfigFile.txt");
var newDict = JsonConvert.DeserializeObject<Dictionary<string, string>>(txt);
}
}
}
和ConfigFile.txt看起来像这样
{"FirstName":"Elmer","LastName":"Fudd","Password":"\a\ansld\sb\b8d95nj"}
如果您希望它更易于阅读,请使用
var json = JsonConvert.SerializeObject(dict, Formatting.Indented);
你会得到
{
"FirstName": "Elmer",
"LastName": "Fudd",
"Password": "\a\ansld\sb\b8d95nj"
}
您可以将整数转换为字符,所以只需这样做...
string[] tokens = fileText.Split((char)0x1e);
// ...
string[] keyValuePair = token.Split((char)0x1f);
...但是将您的密码编码为 base64 会更容易和更清晰...
string base64 = Convert.ToBase64String(passwordHash);
byte[] passwordHash = Convert.FromBase64String(base64);
... 注意:
hashes/encrypted 数据可能包含这些字符,因此我不会将 hases 转储到文本文件中。
以下 class 使用正则表达式提取字符串段并支持包含不可打印字符的密码:0x00 .. 0xFF
class 包含配置段的属性
您可以 运行 演示示例 .NEt Fiddle
using System;
using System.Text.RegularExpressions;
class ConfigParser
{
public string Text { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserId { get; set; }
public string Password { get; set; }
public string Date { get; set; }
public ConfigParser(string text)
{
Text =text;
Parse(text);
}
private static string pattern = @"
^FirstName=(?<firstname>\w+) \|
LastName=(?<lastname>\w+) \|
UserId=(?<userid>\w+) \|
Password=(?<pasword>.+)
Date=(?<date>.+)
$
";
private Regex regex = new Regex(pattern,
RegexOptions.Singleline
| RegexOptions.ExplicitCapture
| RegexOptions.CultureInvariant
| RegexOptions.IgnorePatternWhitespace
| RegexOptions.Compiled
);
private void Parse(string text)
{
Console.WriteLine("text: {0}",text);
Match m = regex.Match(text);
FirstName = m.Groups["firstname"].ToString();
LastName = m.Groups["lastname"].ToString();
UserId = m.Groups["userid"].ToString();
Password = m.Groups["pasword"].ToString();
Date = m.Groups["date"].ToString();
}
}
使用方法:
var text ="your text here";
var c = new ConfigParser(text );
you can access the properties of the class: FirstName, LastName,....
Console.WriteLine("firstname: {0}", c.FirstName);
Console.WriteLine("lastname: {0}", c.LastName);
Console.WriteLine("UserId: {0}", c.UserId);
Console.WriteLine("Password: {0}", c.Password);
Console.WriteLine("date {0}", c.Date);
示例输出:
密码包含不可打印字符 |分隔符和符号
text: FirstName=Elmer|LastName=Fudd|UserId=EFudd|Password=fg%|uy|◄¶|hj↑khg|Date=7/29/2016
firstname: Elmer
lastname: Fudd
UserId: EFudd
Password: fg%|uy|◄¶|hj↑khg
date: 7/29/2016
最简单的答案:
使用 ALT-数字键盘值技巧将特殊字符插入字符串。记录组 ALT-31 (▼) 用于分隔 Key/Value 对的结尾,项目组 ALT-30 (▲) 用于分隔键和值。将字符串保存为 UTF-8。
分隔符代码为
private static char tokenDelimiter = ('▲');
private static char keyValuePairDelimiter = ('▼');
使用相同的 ALT 数字小键盘技巧来放入上下三角形。包括黑色三角形永远不会被编辑或删除的说明,并解释它们的含义。
它让我回到了 DOS 时代。简单,只需 5 分钟即可实现 - 不需要对现有代码库进行实质性更改 - 只需更改两个定界符即可。
我正在开发一个使用纯 ASCII .txt 文件作为 key/value 配置文件的项目。 ConfigFile.txt 的当前格式类似于
名字=埃尔默|姓氏=Fudd|用户 ID=EFudd|密码=fubar|日期=2016 年 7 月 29 日
这很容易读入程序并使用 KeyValuePairs 创建字典,代码如下:
using (FileStream fs = new FileStream("ConfigFile.txt", FileMode.Open))
{
using (StreamReader sr = new StreamReader(fs))
{
string fileText = sr.ReadToEnd();
// Tokenize the entire file string into separate key=value strings.
string[] tokens = fileText.Split('|');
// Iterate through all of the key=value strings, tokenize each one into a key=Value
// pair and add the key and value as separate strings into the dictionary.
foreach (string token in tokens)
{
string[] keyValuePair = token.Split('=');
configDict.Add(keyValuePair[0], keyValuePair[1]);
}
}
}
它首先使用'|'将每个key/value拆分为一个单独的字符串作为分隔符。
名字=埃尔默
姓氏=Fudd
UserId=EFudd
密码=foobar
日期=2016 年 7 月 29 日
然后,对于每个 key/value 字符串,它在 '=' 分隔符上分隔键和值,创建一个 KeyValuePair,并将其插入到字典中供以后在程序中查找。
到目前为止一切顺利。指示用户不要使用任何一个分隔符创建密码。但是,我现在必须在将密码包含在文件中之前对其进行加密,并且加密例程可以生成从 0x20 到 0x7F 的任何可打印字符。因此,加密密码可以以其中一个或两个分隔符结尾。我最终可以得到 'foobar'(或其他)被加密引擎加密成 P#|=g%。这扰乱了 split 函数正常工作的能力。
所以,我想更改输入到记事本 .txt 文件中的分隔符来控制字符,而不是“|”分隔符,我正在使用 0x1E(记录分隔符)并将“=”符号替换为 0x1F(单元分隔符)。
我可以直接在 C# 中进行转义和编码,没有任何问题,但是我如何修改原始的 .txt 磁盘文件,以便它能够正确读取分隔符作为单个(不可打印的)字符?
因此,我不会使用那样的纯文本,而是使用适当的序列化格式,例如 JSON。
有一些工具可以为您完成艰苦的工作。
内置的 System.Web.Script.Serialization
命名空间有一些工具可以使用,但我更喜欢使用 Json.Net。如果您有 Visual Studio,您可以使用 nuGet 安装它(如果您需要更多帮助,请在评论中告诉我)。
但是一旦你将它添加到你的项目中,你就可以做这样的事情
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
namespace ConsoleApplication1
{
public class Program
{
static void Main(string[] args)
{
var dict = new Dictionary<string, string>();
dict.Add("FirstName", "Elmer");
dict.Add("LastName", "Fudd");
dict.Add("Password", @"\a\ansld\sb\b8d95nj");
var json = JsonConvert.SerializeObject(dict);
File.WriteAllText("ConfigFile.txt, json);
var txt = File.ReadAllText("ConfigFile.txt");
var newDict = JsonConvert.DeserializeObject<Dictionary<string, string>>(txt);
}
}
}
和ConfigFile.txt看起来像这样
{"FirstName":"Elmer","LastName":"Fudd","Password":"\a\ansld\sb\b8d95nj"}
如果您希望它更易于阅读,请使用
var json = JsonConvert.SerializeObject(dict, Formatting.Indented);
你会得到
{
"FirstName": "Elmer",
"LastName": "Fudd",
"Password": "\a\ansld\sb\b8d95nj"
}
您可以将整数转换为字符,所以只需这样做...
string[] tokens = fileText.Split((char)0x1e);
// ...
string[] keyValuePair = token.Split((char)0x1f);
...但是将您的密码编码为 base64 会更容易和更清晰...
string base64 = Convert.ToBase64String(passwordHash);
byte[] passwordHash = Convert.FromBase64String(base64);
... 注意: hashes/encrypted 数据可能包含这些字符,因此我不会将 hases 转储到文本文件中。
以下 class 使用正则表达式提取字符串段并支持包含不可打印字符的密码:0x00 .. 0xFF class 包含配置段的属性
您可以 运行 演示示例 .NEt Fiddle
using System;
using System.Text.RegularExpressions;
class ConfigParser
{
public string Text { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserId { get; set; }
public string Password { get; set; }
public string Date { get; set; }
public ConfigParser(string text)
{
Text =text;
Parse(text);
}
private static string pattern = @"
^FirstName=(?<firstname>\w+) \|
LastName=(?<lastname>\w+) \|
UserId=(?<userid>\w+) \|
Password=(?<pasword>.+)
Date=(?<date>.+)
$
";
private Regex regex = new Regex(pattern,
RegexOptions.Singleline
| RegexOptions.ExplicitCapture
| RegexOptions.CultureInvariant
| RegexOptions.IgnorePatternWhitespace
| RegexOptions.Compiled
);
private void Parse(string text)
{
Console.WriteLine("text: {0}",text);
Match m = regex.Match(text);
FirstName = m.Groups["firstname"].ToString();
LastName = m.Groups["lastname"].ToString();
UserId = m.Groups["userid"].ToString();
Password = m.Groups["pasword"].ToString();
Date = m.Groups["date"].ToString();
}
}
使用方法:
var text ="your text here";
var c = new ConfigParser(text );
you can access the properties of the class: FirstName, LastName,....
Console.WriteLine("firstname: {0}", c.FirstName);
Console.WriteLine("lastname: {0}", c.LastName);
Console.WriteLine("UserId: {0}", c.UserId);
Console.WriteLine("Password: {0}", c.Password);
Console.WriteLine("date {0}", c.Date);
示例输出: 密码包含不可打印字符 |分隔符和符号
text: FirstName=Elmer|LastName=Fudd|UserId=EFudd|Password=fg%|uy|◄¶|hj↑khg|Date=7/29/2016
firstname: Elmer
lastname: Fudd
UserId: EFudd
Password: fg%|uy|◄¶|hj↑khg
date: 7/29/2016
最简单的答案:
使用 ALT-数字键盘值技巧将特殊字符插入字符串。记录组 ALT-31 (▼) 用于分隔 Key/Value 对的结尾,项目组 ALT-30 (▲) 用于分隔键和值。将字符串保存为 UTF-8。
分隔符代码为
private static char tokenDelimiter = ('▲');
private static char keyValuePairDelimiter = ('▼');
使用相同的 ALT 数字小键盘技巧来放入上下三角形。包括黑色三角形永远不会被编辑或删除的说明,并解释它们的含义。
它让我回到了 DOS 时代。简单,只需 5 分钟即可实现 - 不需要对现有代码库进行实质性更改 - 只需更改两个定界符即可。