我有以下文件:
<?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.net-->
<string name="test" >- test</string>
<string name="test" >test-test</string>
<string name="test" >test - test</string>
我想en dash
用它的 unicode 值替换 ,但不是全部,只是string
标记中的一个
我用不同的正则表达式运行了几个sed
,但我无法弄清楚。其中之一是
sed -i.bak "s/-[^-\<\>0-9]/\–\;/g" strings.xml
输出是:
<?xml version="1.0" encoding="utf-8"?>
<!-–enerated by-->
<string name="test" >–test</string>
<string name="test2" >test–est</string>
<string name="test3" >test –test</string>
我的问题是也在更换空白处和第一个字符第二个词的。我对regex
和没有那么丰富的经验sed
。你能解释一下我做错了什么吗?
注意:我使用的是 OSX。
答案1
使用最近的( for\K
和s///r
)perl
并假设您的<string>
标签不嵌套:
perl -0777 -pi.bak -e's{<string.*?>\K.*?(?=</string>)}{$&=~s/-/–/rg}ges' file.xml
-0777
:吸食模式:一次处理整个文件(以允许<string>
标签跨越多行)。-p
:sed
模式-i.bak
:带有扩展的就地编辑.bak
(顺便说一句,这就是一些sed
实现的想法来源)s{...}{...}ges
:全局替换 (g
),其中.
也匹配换行符 (s
),并将替换视为perl
要执行的代码 (e
)。<string.*?>\K.*?</string>
: 匹配 from<string...>
to</string>
但不将标签本身包含在 is 的部分中匹配的(\K
定义了其中匹配的部分开始,并且(?=...)
是一个前瞻运算符,仅检查if</string>
存在,但不将其包含在匹配中)。$&=~s/.../.../rg
。进行替换匹配的部分 ($&
)。该r
标志实际上不修改$&
而是返回替换的字符串。
答案2
唷,过了一段时间我就明白了。这是一个幼稚的解决方案。特登的回答更正确,你应该使用他的:)。
sed -Ei.bak "s/(.*<string[^>]*\")(.*)-(.*)/\1\2\–\3/g" strings.xml
我在用反向引用引用先前匹配的字符串。这些都是\1
\2
等等
在这种情况下 sed 应匹配以下组:
(.*<string[^>]*\")
- 任何字符后跟一个字符串标记,直到引号为止"
。第 1 组(.*)
- 之后"
(包括现在>
)直到第 3 组的任何内容。第2组-
匹配的破折号(.*)
- 匹配破折号后的任何内容第3组
然后,我将其替换为之前匹配的组和破折号 HTML 值–
,并使用\n
withn
作为对组的引用n
。
问题:
我目前正在尝试解决一些问题,所以请配合我:
- 第 1 组比赛也
dsfjpasj<string
- 第 1 组应包含字符串标记结束字符
>
>1 -
正如 terdon 指出的那样:“这对于拥有或嵌套标签或跨越多行的标签的情况不起作用”
阅读更多:
http://toytoygogie.blogspot.de/2010/02/using-sed-with-backreference-as.html
答案3
如果我理解正确,您想要替换标记-
内的所有情况(示例中为三个)<strng></string>
并且仅替换这些情况。如果是这样,这些方法应该有效假设你的 XML 是正常的:
使用正则表达式和简单的工具,例如
sed
sed 's/\(<string[^>]*>[^-]*\)-\([^-]*<\/string\)/\1\–\2/' file.xml
如果你的文件是总是就像上面的例子,你可以确定你的标签将永远是
<string name="test" ></string>
,你可以使用回顾:perl -pe 's/(?<=<string name="test" >)([^<]*?)-([^<]*)/$1–$2/g' file.xml
-
如果标签内有多个标签,则以上方法都不起作用。为了处理这种情况,您可以编写一个简单的小脚本来检查我们是否在<string></string>
标签内。这也应该处理嵌套标签。perl -F'<' -lane 'for($i=0;$i<=$#F;$i++){ $a++ if $F[$i]=~/^string/; $F[$i]=~s/-/–/g if $a>0; $a-- if $F[$i]=~/^\/string/ } print join "<",@F' file.xml